Open28

【Flutter】

状態管理

provider + ChangeNotifier

provider = 親Widgetから子Widgetへのデータ受け渡しを行うためのパッケージ
ChangeNotifier = 変更通知が行えるクラス(Flutterが標準で提供している)

providerの中にChangeNotifierProviderというクラスがあり、ChangeNotifierの変更をlistenし、子Widgetへと変更を通知することができる

  • インスタンス化して子に渡す場合
Provider(
  create: (context) => MyWidget(),
  child: ...
)
  • すでに生成されたインスタンスを渡す場合
MyWidget myWidget = MyWidget()

Provider.value(
  value: myWidget,
  child: ...
)

Provider()(またはProvider.value())で渡した値は、Provider.of(context)で、コンテキストから参照できる。

提供側で使用するクラスによっては値が動的に変化する。(ChangeNotifierProviderやStreamProviderなど)
その変更を下位のWidgetで監視するためには、Provider.of(context, listen: true)とする。(デフォルトはlisten=trueなのでわざわざ書かなくてよい。)
逆に変更を監視する必要がなく、Widgetのリビルドを避けたい場合はlisten: falseにする。

provider v4.1.0から、Provider.ofの代わりに、BuildContextのExtentionメソッドが追加されている。
Provider.of(context, listen: false) => context.read()
Provider.of(context, listen: true) => context.watch()

複数あるデータのうち一部のみを選択的に監視するためにはSelector()を使用する。
provider v4.1.0からはより簡潔に書ける方法としてcontext.select()がある。

Extentionメソッド

既存のクラスにメソッドを追加することができる機能

画面遷移

名前付きルート

画面の差し替え

この方法で遷移すると前の画面には戻れない。

Navigator.of(context).pushReplacementNamed('/hoge');
Navigator.pushReplacementNamed(context, '/hoge');

スタック

  • 画面が順に積まれていくため、前の画面に戻ることができる。
Navigator.of(context).pushNamed("/hoge");
Navigator.pushNamed(context, '/hoge');

// 前の画面に戻る
Navigator.of(context).pop();
Navigator.pop(context);
  • 指定した画面まで戻る。
Navigator.popUntil(context, ModalRoute.withName("/hoge"));
  • 指定した画面までpopして、新規画面をpushする。
// '/'まで戻って、Hoge()を表示
Navigator.of(context).pushNamedAndRemoveUntil(
 '/hoge',
 ModalRoute.withName('/'),
);
Navigator.pushNamedAndRemoveUntil(
 context,
 '/hoge',
 ModalRoute.withName('/'),
);

// 全画面popして、Hoge()を表示
Navigator.of(context).pushNamedAndRemoveUntil(
 '/hoge',
 (route) => false,
);
Navigator.pushNamedAndRemoveUntil(
 context,
 '/hoge',
 (route) => false,
);

名前で指定しない場合

MaterialPageRouteでルートを取得する

Navigator.of(context).push(
  MaterialPageRoute(
    builder: (context) {
      return SecondPage();
    },
  ),
);
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) {
      return SecondPage();
    },
  ),
);
  • 指定した画面までpopして、新規画面をpushする。
// '/'まで戻って、Hoge()を表示
Navigator.of(context).pushAndRemoveUntil(
 MaterialPageRoute(builder: (BuildContext context) => Hoge()),
 (Route<dynamic> route) => route is Hoge,
);
Navigator.pushAndRemoveUntil(
 context,
 MaterialPageRoute(builder: (BuildContext context) => Hoge()),
 (Route<dynamic> route) => route is Hoge,
);

// 全画面popして、Hoge()を表示
Navigator.of(context).pushAndRemoveUntil(
 MaterialPageRoute(builder: (BuildContext context) => Hoge()),
 (Route<dynamic> route) => route is Hoge,
);
Navigator.pushAndRemoveUntil(
 context,
 MaterialPageRoute(builder: (BuildContext context) => Hoge()),
 (Route<dynamic> route) => route is Hoge,
);

漢字が中華フォントにならないようにする

flutter_localizationsパッケージをインストール

import 'package:flutter_localizations/flutter_localizations.dart';

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      // ここから
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: [
        Locale('ja'),
      ],
      // ここまで
      ...
}
``

クリックイベントを付与する

クリックイベントを付与するためのWidget(GestureDetector, InkWell, InkResponse)でラップする。

GestureDetector(
  onTap: () { ... },
  child: Card( ...

iPhoneのシミュレータでソフトウェアキーボードが表示できないとき

Simulatorメニューバー > I/O > Keyboard > Connect Hardware Keyboardのチェックを外す。

キーボードを表示したときのWidgetのサイズについて

  • Scaffoldのサイズやレイアウトを変更したくない場合
    resizeToAvoidBottomInset: false,
  • ModalBottomSheetでキーボードに追従させる
    isScrollControlledとSingleChildScrollViewを使う
showModalBottomSheet(
  context: context,
  isScrollControlled: true,
  builder: (context) => SingleChildScrollView(
    child: Container(
    ...

画面下から出てくるモーダル
isScrollControlled: trueと、子Widgetのheightを設定することで任意の高さにできる。

showModalBottomSheet(
  context: context,
  isScrollControlled: true,
  builder: (context) => Container(
    height: MediaQuery.of(context).size.height * 0.85,
    decoration: BoxDecoration(
      borderRadius: BorderRadius.only(
        topLeft: const Radius.circular(25.0),
        topRight: const Radius.circular(25.0),
      ),
    ),
    child: ...

ListView

ListView.builder(
  itemCount: list.length, // 生成するデータの個数を指定
  itemBuilder: (context, index) {
    return Container( ...

生成されるWidgetの間にラインなどを引きたい場合

ListView.separated(
  separatorBuilder: (BuildContext context, int index) => Divider(),
  ...
作成者以外のコメントは許可されていません