「.NET 開発基盤部会 Wiki」は、「Open棟梁Project」,「OSSコンソーシアム .NET開発基盤部会」によって運営されています。
目次 †
概要 †
Flutter の step by step(其の一)。
手順1:インストールから実行まで。 †
コチラを参考にして実施
インストール †
ダウンロード †
https://flutter.dev/docs/get-started/install/windows
解凍&パス指定 †
flutter_windows_x.x.x-stable.zip
- 解凍
C:\prog\dev\flutter
- パス指定
// ユーザーの環境変数に設定する例
$path =[System.Environment]::GetEnvironmentVariable('PATH','User')
$path +=";" + "C:\prog\dev\flutter\bin";
[System.Environment]::SetEnvironmentVariable('PATH',$path,'User')
チェック(1) †
以下のCommandを実行し、TODOを確認する。
>flutter doctor
- 既定値でインストール(Android Virtual Device(AVD)もインストール)。
- 初期設定
- 必要に応じて、Android Targetをインストール
- 日本語化したら問題が出るらしい(現時点で解決しているか不明)。
※ 前述のPowerShell?だと、%USERPROFILE%が展開されねぇんだけど...。
プラグインのインストール †
「File」メニュー -> 「Settings..」 -> 「Plugins」
- Flutterプラグインの「Install」ボタンを押下
- Dartプラグインの「Install」ボタンを押下
チェック(2) †
以下のCommandを実行し、問題が無いことを確認する。
>flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 2.0.3, on Microsoft Windows [Version
10.0.19041.867], locale ja-JP)
[√] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[√] Chrome - develop for the web
[√] Android Studio (version 4.1.0)
[√] VS Code, 64-bit edition (version 1.47.2)
[√] Connected device (2 available)
• No issues found!
>
余談:SDKのパス変更 †
SDKのパスを変更する場合、以下の手順に従って変更する。
サンプルの生成と実行 †
新規作成プロジェクト †
Android Studioで、新規作成プロジェクトする。
- 「Start Flutter New Project」
or 「File -> New -> New Flutter Project..」
プロジェクトタイプ | 概要 |
Flutter Application | アプリケーション用 |
Flutter Plugin Android | ネイティブAPI用ライブラリ用 |
Flutter Package | 画面部品用 |
Flutter Module | Dartライブラリ用 |
- 以下を入力する。
入力項目 | 概要 |
Project Name | プロジェクト名 |
Flutter SDK path Project location | Flutter SDKをインストールしたフォルダを指定。 |
Description | プロジェクト、アプリの説明 |
Package Name | 通常、ドメインを反対にした値を入力 |
デバッグ実行 †
- 実行するデバイスでChromeなどのブラウザを選択し、
- 緑三角(実行)、虫(デバッグ)ボタンを押すと実行。
- ブラウザではなく、エミュレータや実機を使う手順はコチラ。
プロジェクトの開発 †
以降の手順で説明する。
デバッグ実行 †
Android Studioで1GB、エミュレータで6GB程、メモリを消費するので、
開発機のメモリが16GB程度では少々、スペック不足、故に実機でのデバッグを行うことになる。
エミュレータを登録して実行 †
- エミュレータの登録
- 「Tools」メニュー -> 「AVD Manager」
- (「Create Virtual Device ..」でデバイスを選択し「Next」ボタンを押下)
- (API Levelを選択(最新に近いモノを選択する))
- 「Actions」から三角ボタンを押して、エミュレータを起動
- エミュレータでデバッグ実行
- 実行するデバイスを選択し、
- 緑三角(実行)、虫(デバッグ)ボタンを押すと実行。
実機でデバッグ実行する場合 †
- マニュアル
- 「Build」メニュー -> 「Flutter」 -> 「Build APK」
を実行して、apkファイルをプロジェクト出力。
Flutter Devtoolsを利用する。 †
- 「Tools」メニュー -> 「Flutter」 -> 「Open Flutter Devtools」を実行して、
- デバッグ実行中に、[Flutter Inspector]のペインを開くとUI要素のツリーが表示される。
- Flutter Devtoolsでは、その他、様々なツールを使用できる。
- Timeline
- Memory
- Performance
- Debugger
- Logging
その他のツールを利用する。 †
- 外部デザイナとして、Flutter Studioがある。
を併用すると、RAD風に開発できる。
手順2:イベントを実装する。 †
ボタンのWidget †
- 以下のようなボタンがある。
- 「() {}」(空のラムダ式)を、
生成されたコード中にある、
「_incrementCounter」に変更してテスト可能。
RaisedButton? †
RaisedButton(
child: const Text('Button'),
color: Colors.orange,
textColor: Colors.white,
onPressed: () {},
),
ElevatedButton? †
onPressedに、任意のメソッドを書く。
ElevatedButton(
child: const Text('Button'),
style: ElevatedButton.styleFrom(
primary: Colors.orange,
onPrimary: Colors.white,
),
onPressed: () {},
),
FlatButton? †
FlatButton(
child: const Text('Button'),
textColor: Colors.black,
onPressed: () {},
),
TextButton? †
onPressedに、任意のメソッドを書く。
TextButton(
child: const Text('Button'),
style: TextButton.styleFrom(
primary: Colors.black,
),
onPressed: () {},
),
OutlineButton? †
OutlineButton(
child: const Text('Button'),
onPressed: () {},
),
OutlinedButton? †
onPressedに、任意のメソッドを書く。
OutlinedButton(
child: const Text('Button'),
style: OutlinedButton.styleFrom(
primary: Colors.black,
),
onPressed: () {},
),
IconButton? †
ButtonBar? †
FloatingActionButton? †
PopupMenuButton? †
DropdownButton? †
任意のWidget †
ボタンでない、任意のWidgetに、イベントを実装できる。
InkWell? †
簡単に、Ripple エフェクトを付ける事が出来る。
Material( // 親にMaterialが必須
color: Colors.blue, // Material自体は青を指定
child: Center( //
child: Ink( // ここをContainerにするとsplashが効かないので、Inkに変える
color: Colors.yellow, // 背景色はここで指定
width: 200.0,
height: 100.0,
child: InkWell(
onTap: () {},
child: Center(child: Text('YELLOW'))
),
),
),
)
GestureDetector? †
InkWellのRipple エフェクトは無いが、
他のイベント(ジェスチャー)にも反応する。
GestureDetector(
// タッチ検出対象のWidget
child: Text(
'How to use GestureDetector',
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.bold),
),
// ダブルタップ
onDoubleTap: _incrementCounter
)
参考 †
Qiita †
手順3:画面遷移処理を実装する。 †
- 画面遷移の実装について。
- MaterialApp?以下で実装。
- MaterialApp? が持っている Navigator を使って画面遷移。
- Navigatorの動作イメージはStackで、一番上の Route が現在の画面。
- 無限にPushできてしまうので、実装を考える必要がある。
- 常にPoP & Pushだと、戻った時に、常に状態は保存されていない。
- ...
API †
push †
- Widget を MaterialPageRoute? に渡して Route を作る。
- Navigator.pushにRouteを渡して画面遷移。
Navigator.of(context).pop();
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) {
return MyHomePage(title: 'Flutter Demo Home PageA');
},
),
);
pushNamed †
- 予め MaterialApp? に Routes を登録しておく。
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
...
),
routes: {
'/': (context) => MyHomePage(title: 'Flutter Demo Home Page'),
'/message': (context) => MessageView(),
- Navigator.pushNamedにルート名を渡して画面遷移。
メニュー †
AppBar? †
にNavigatorによる画面遷移を書く。
- MaterialPageRoute?のbuilderが返すWidgetにMyHomePage?を流用。
Drawer †
- 以下を参考に、ListTile?のonTap:にNavigatorによる画面遷移を書く。
- MaterialPageRoute?のbuilderが返すWidgetにMyHomePage?を流用。
TabBar? †
- 以下は、TabPage?が遷移先で、Navigatorは使用しない。
- _MyHomePageState?.buildメソッドが
Scaffoldではなく、DefaultTabController?を返すのもポイント。
BottomNavigationBar? †
- 以下は、PageWidget?が遷移先で、Navigatorは使用しない。
- bottomNavigationBar?のonTap:にIndex切替処理を書く。
参考 †
Qiita †
手順4:レイアウトを行う。 †
body, children or childと、レイアウトを行う。
Layout系 †
Container †
- デフォルトで
- Columnは縦方向いっぱい
- Rowは横方向いっぱい
のサイズになる。
body: Container(
...
child: Column or Row
Center †
- 中央に寄るのはColmn(Row)自体。
- 内部コンテンツの位置は変わらない。
body: Center(
...
child: Column or Row
その他 †
- Align
- Padding
- FittedBox? / BoxFit?
- SafeArea?
Multi Layout系 †
Colmn †
- 列内の行は縦にスタック
- 行内の内部コンテンツは横にスタック
body: Column(
...
children: <Widget>[
Row(
Row †
- 行内の列は横にスタック
- 列内の内部コンテンツは縦にスタック
body: Row(
...
children: <Widget>[
Column(
その他 †
- Expanded
- Stack
- ListView? / ListTile?
- TabBar? / Tab / TabBarView?
- BottomNavigationBar?
margin/padding †
margin、paddingプロパティ †
- EdgeInsets?には以下のメソッドがある。
- only:上下左右のmargin/paddingを指定
- fromLTRB:onlyの名前無し引数版(上下左右)
- symmetric:縦方向横方向のmargin/paddingを指定
- all:全方向のmargin/paddingを指定
margin、paddingプロパティが無い場合 †
RowやColumnなど、childrenプロパティのあるWidgetは、
margin、paddingプロパティを持たないが、
Containerをネストして使用すると面倒なので、
SizedBox?を拡張したSpaceBox?を使用すると良い。
- 参考
- Padding(またはMargin)だけが目的でContainerを
使うのは間違ってる(というか分かりづらい) -Flutter-
AxisAlignment? †
内部コンテンツの位置
MainAxisAlignment? †
...と書き、主軸方向の配置位置を調整する。
- start/end
先頭/末尾寄せ
- center
中央寄せ
- spaceEvenly
間隔を均等に配置
- spaceAround
各余白を均等に配置
- spaceBetween
最大限に分散配置
CrossAxisAlignment? †
...と書き、交差軸方向の配置位置を調整する。
- start/end
先頭/末尾寄せ
- center
中央寄せ
- stretch
引き伸ばして配置
- baseline
ベースラインを揃えて配置
- SizedBox?.expand
交差軸方向のスペースをすべて取らない場合、
CrossAxisAlignment?が適切に機能しない事がある。
この場合は、SizedBox?.expandで、スペースを消費させる。
body: SizedBox.expand(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
...,
],
),
),
参考 †
手順5:パッケージの追加と利用 †
english_wordsパッケージの例 †
パッケージの追加 †
english_wordsパッケージを追加・利用する。
- その後、ツール・メニューかコマンドから、Pub Getを実行。
- インストールされ、pubspec.lockにも記載が追加される。
パッケージの利用 †
- ライブラリを利用する(コチラのコードを修正)。
class _MyHomePageState extends State<MyHomePage> {
String _counter = 'hoge';
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter = WordPair.random().asPascalCase;
});
}
データの永続化(shared_preferences) †
パッケージの追加 †
shared_preferencesパッケージを追加する必要がある。
パッケージの利用 †
import 'package:shared_preferences/shared_preferences.dart';
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
late Future<int> _counter;
Future<void> _incrementCounter() async {
final SharedPreferences prefs = await _prefs;
final int counter = (prefs.getInt('counter') ?? 0) + 1;
setState(() {
_counter = prefs.setInt("counter", counter).then((bool success) {
return counter;
});
});
}
@override
void initState() {
super.initState();
_counter = _prefs.then((SharedPreferences prefs) {
return (prefs.getInt('counter') ?? 0);
});
}
参考 †
ブラウザ起動(url_launcher) †
パッケージ追加 †
url_launcherパッケージを追加する必要がある。
パッケージの利用 †
import 'package:url_launcher/url_launcher.dart';
_launchURL() async {
const url = "http://https://www.google.co.jp/";
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not Launch $url';
}
}
参考 †
WebAPIを呼び出す。 †
パッケージ追加 †
HTTPパッケージを追加する必要がある。
GET †
- Example
https://pub.dev/packages/http/example
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
// This example uses the Google Books API to search for books about http.
// https://developers.google.com/books/docs/overview
var url =
Uri.https('www.googleapis.com', '/books/v1/volumes', {'q': '{http}'});
// Await the http get response, then decode the json-formatted response.
var response = await http.get(url);
if (response.statusCode == 200) {
var jsonResponse =
convert.jsonDecode(response.body) as Map<String, dynamic>;
var itemCount = jsonResponse['totalItems'];
print('Number of books about http: $itemCount.');
} else {
print('Request failed with status: ${response.statusCode}.');
}
- 作法
POST †
- async/await
var response = await http.post(
"https://www.example.com/path/to/api",
body: {
"nickname": "chooyan",
"description": "A freelance mobile app developer",
},
);
- application/x-www-form-urlencoded
bodyに、Map<String, String>型 の変数を指定する。
- application/json
- JSON形式のString型を指定する。
- json.encode(Map<String, String>型)でもOK。
その他 †
- ヘッダ
headersに、Map<String, String>型 の変数を指定する。
参考 †
参考 †