Open21
ゼロから始める Flutter
Flutter SDK をインストール
SDK を好きなところにダウンロードする
export コマンドで環境変数を設定する
ターミナルを起動、ダウンロードした SDK のパスにあわせて環境変数を設定する
cd [インストールしたディレクトリ]
export PATH="$PATH:`pwd`/flutter/bin"
flutter doctor を実行
flutter doctor
- flutter doctor を実行すると環境を自動的に確認してくれる
- ターミナルにレポート結果が表示される
- 不足しているソフトや必要なタスクが表示される
e.g.
iOS の環境セットアップ
- Xcode の最新版を入手する(AppStore など)
- ターミナルを起動してダウンロードした Xcode コマンドラインツールを利用できるように以下のコマンドを実行する
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
sudo xcodebuild -runFirstLaunch
- Xcode 起動するか、以下のコマンドでライセンスの同意を行う
sudo xcodebuild -license
- iOS シミュレーターを起動したい場合は以下のコマンドで起動できる
open -a Simulator
Flutter アプリをまずは作ってみる
- 好きなディレクトリで以下のコマンドを実行
flutter create flutter_example
(中略)
All done!
In order to run your application, type:
$ cd flutter_example
$ flutter run
Your application code is in flutter_example/lib/main.dart.
- 作成されたディレクトリに移動して以下のコマンドを実行
cd flutter_example
flutter run
- 転送したいデバイスを選択する
- iOS の証明書を選択する
- ビルドが実行される
- 実機でデモアプリが起動する
Flutter のウィジェットカタログ
よりわかりやすくまとめられた物
"iproxy"は、開発元を検証できないため開けません と表示される場合
- Macの「システム環境設定」から「セキュリティーとプライバシー」を開く
- 一般の最下部に許可ボタンが表示されているので許可する
- これを許可しないと
Hot reload
やHot restart
の機能を利用することができない
flutter create で生成されるサンプルコードはこういう感じ
lib/main.dart
import 'package:flutter/material.dart'; // マテリアル UI がまとめられたパッケージ
void main() { // アプリケーションのエントリーポイント
runApp(const MyApp()); // アプリを実行するための runApp 関数を MyApp を引き渡して実行
}
class MyApp extends StatelessWidget { // ウィジェットを返すクラス Stateless = 状態をもたないウィジェット
const MyApp({Key? key}) : super(key: key); // コストラクタ
@override // アノテーション(注釈)
Widget build(BuildContext context) { // 画面を描画するためのメソッド
return MaterialApp( // Flutter アプリケーション全体を管理するためのもの
title: 'Flutter Demo',
theme: ThemeData( // テーマ
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page') , // MyHomePage という別のウィジェット
);
}
}
class MyHomePage extends StatefulWidget { // 状態を持つウィジェット
const MyHomePage({Key? key, required this.title}) : super(key: key); // コストラクタ
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState(); // MyHomePageState と呼ばれる State を作成
}
// MyHomePageState の定義
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() { //カウンターの加算処理
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) { // State が変わるたびにこの build メソッドが実行される
return Scaffold( // 基盤となるウィジェット
appBar: AppBar( // トップバー
title: Text(widget.title), // MyHomePage オブジェクトの title を AppBar に設定
),
body: Center( // 本体部分、中央寄せのウィジェット
child: Column( // children のリストを受け取って縦に並べる、横の場合は Row ウィジェットを使う
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton( // フローティングボタン
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
末尾のカンマは大事
Flutter のレンダリングに関わる Widget、Element、RenderObject
ツリーは 3種類、この3つのツリーが組み合わせって UI ができている
- Widget ツリー // 開発者が記述するもの
- Element ツリー // Widget と RenderObject の仲介役
- RenderObject ツリー // レイアウト、描画を行う
Widget
- UI の構成要素と子 Widget を保持している immutable なオブジェクト
Widget は 4種類
- StatelessWidget
- StatefulWidget
- InheritedWidget
- RenderObjectWidget
Element
- state をもつ mutable なオブジェクト
- Widget は createElement メソッドで Element を生成する
- Widget が Widget ツリーに挿入されると Element が生成される
Widget と Element の関係
- StatelessWidget => ComponentElement
- StatefulWidget => ComponentElement
- InheritedWidget => ComponentElement
- RenderObjectWidget => RenderObjectElement
RenderObject
- UI のレイアウトと描画を行う mutable なオブジェクト
- Element ツリーが構築されるときに、Element が RenderObjectElement の場合に RenderObject のツリーが作られる
(寄り道)一番最初にこれを読むとよりスムーズ
Flutter 公式チャンネル
今週のウィジェットの紹介
YouTubeのvideoIDが不正です
状態管理に関する記事
- 各種状態管理のまとめ的記事
- Provider と Riverpod をカウンターサンプルで比べる
- Provider + ChangeNotifier
- StateNotifier + freezed + Riverpod
- StateNotifier + freezed + Riverpod + Flutter Hooks
Flutter 学習のためのロードマップ
通信ライブラリ
基本の http パッケージ
dio // axios みたいなやつ
Lottie for Flutter
基本の ListView
大量データ突っ込む場合はパフォーマンスが問題になりそうだが、そのとき考える
通信〜コンポーネントのレンダリングの流れ
- fetchData で json 取得
- setState でローディングアイコンの切り替え
- ListTile で UITableViewCell のようなテーブル内のセルを定義
- CircularProgressIndicator でローディングを表示
- Image.networkでネットワーク越しの画像表示が可能
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_samples/fetch_data/photo.dart';
import 'package:http/http.dart' as http;
class MainFetchData extends StatefulWidget {
_MainFetchDataState createState() => _MainFetchDataState();
}
class _MainFetchDataState extends State<MainFetchData> {
List<Photo> list = [];
var isLoading = false;
_fetchData() async {
setState(() {
isLoading = true;
});
final response = await http
.get(Uri.parse("https://jsonplaceholder.typicode.com/photos"));
if (response.statusCode == 200) {
list = (json.decode(response.body) as List)
.map((data) => new Photo.fromJson(data))
.toList();
setState(() {
isLoading = false;
});
} else {
throw Exception('Failed to load photos');
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Fetch Data JSON"),
),
bottomNavigationBar: Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
child: Text("Fetch Data"),
onPressed: _fetchData,
),
),
body: isLoading
? Center(
child: CircularProgressIndicator(),
)
: ListView.builder(
itemCount: list.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
contentPadding: EdgeInsets.all(10.0),
title: Text(list[index].title!),
trailing: Image.network(
list[index].thumbnailUrl!,
fit: BoxFit.cover,
height: 40.0,
width: 40.0,
),
);
}));
}
}
[!] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
✗ cmdline-tools component is missing
Run `path/to/sdkmanager --install "cmdline-tools;latest"`
See https://developer.android.com/studio/command-line for more details.
✗ Android license status unknown.
Run `flutter doctor --android-licenses` to accept the SDK licenses.
See https://flutter.dev/docs/get-started/install/macos#android-setup for more details.
Flutter for React Native developers
React Native とのコードを交えながら Flutter を説明してくれている
100画面を超える規模のアプリを開発してみた感想のお話
JSON シリアライズ
手動によるシリアライズ
-
dart:convert
に内蔵されているjsonDecode()
を使用する -
JSON 文字列
=>jsonDecode()
関数 =>Map<String,dynamic>
で必要な値を取得できる
Map<String, dynamic> user = jsonDecode(jsonString);
ただし、プロジェクトが大きくなり管理が大変
存在しない JSON フィールドにアクセスすると実行時にエラーになる
コード生成による自動シリアライズ
- json_serializable、built_value によりモデルクラスから定型のコードを出力する
ただし、初期設定が必要になる
json_serializable による自動シリアライズ
- pubspec.yaml に以下を追加
dependencies:
json_annotation: <latest_version>
dev_dependencies:
build_runner: <latest_version>
json_serializable: <latest_version>
-
flutter pub get
を実行 - お作法に則り、モデルクラスを作る
-
flutter pub run build_runner build
orflutter pub run build_runner watch
(監視付きビルド) を実行する