🙈

【Flutter/Dart】今週のWidget(2022Jul_1W)

2022/07/10に公開

はじめに

今週は少し時間がなかったのとちょうど気になったやつだったので、
今週のWidgetについてまとめてみた。

https://www.youtube.com/watch?v=y9xchtVTtqQ

NavigationRail

タブレット用のナビゲーションバーで高さと幅で幅が大きい場合はNavigationRailを使って、高さの方が大きい場合はBottomNavigationBarを使うような感じだ

実装してみた

navirail_page.dart
class NaviRailPage extends StatefulWidget {
  const NaviRailPage({super.key});

  
  State<NaviRailPage> createState() => _NaviRailPageState();
}

class _NaviRailPageState extends State<NaviRailPage> {
  int _selectedIndex = 0;
  // リアルタイムにWidgetを取得するために宣言
  final GlobalObjectKey _globalKey = const GlobalObjectKey('sample');
  bool isTablet = true;

  
  void initState() {
    super.initState();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
    // NotificationListenerでサイズを画面サイズを変更する度に
    // gotNotificationで値をListenする
      body: NotificationListener<SizeChangedLayoutNotification>(
        onNotification: gotNotification,
        child: SizeChangedLayoutNotifier(
          key: _globalKey,
          child: Row(
            children: <Widget>[
              isTablet
                  ? NavigationRail(
                      selectedIndex: _selectedIndex,
                      onDestinationSelected: (int index) {
                        setState(() {
                          _selectedIndex = index;
                        });
                      },
                      labelType: NavigationRailLabelType.selected,
                      destinations: const <NavigationRailDestination>[
                        NavigationRailDestination(
                          icon: Icon(Icons.favorite_border),
                          selectedIcon: Icon(Icons.favorite),
                          label: Text('First'),
                        ),
                        NavigationRailDestination(
                          icon: Icon(Icons.bookmark_border),
                          selectedIcon: Icon(Icons.book),
                          label: Text('Second'),
                        ),
                        NavigationRailDestination(
                          icon: Icon(Icons.star_border),
                          selectedIcon: Icon(Icons.star),
                          label: Text('Third'),
                        ),
                      ],
                    )
                  : const SizedBox.shrink(),
              isTablet
                  ? const VerticalDivider(thickness: 1, width: 1)
                  : const SizedBox.shrink(),
              // This is the main content.
              Expanded(
                child: Center(
                  child: Text('selectedIndex: $_selectedIndex'),
                ),
              ),
            ],
          ),
        ),
      ),
      bottomNavigationBar: !isTablet
          ? BottomNavigationBar(
              items: const <BottomNavigationBarItem>[
                BottomNavigationBarItem(
                  icon: Icon(Icons.favorite_border),
                  activeIcon: Icon(Icons.favorite),
                  label: 'First',
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.bookmark_border),
                  activeIcon: Icon(Icons.book),
                  label: 'Second',
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.star_border),
                  activeIcon: Icon(Icons.star),
                  label: 'Third',
                ),
              ],
            )
          : null,
    );
  }

  bool gotNotification(SizeChangedLayoutNotification notification) {
    try {
      final height = _globalKey.currentContext?.size?.height;
      final width = _globalKey.currentContext?.size?.width;
      if (height != null && width != null) {
        if (mounted && isTablet != (height <= width)) {
          Future.delayed(const Duration(milliseconds: 200), () {
            isTablet = (height <= width);
            setState(() {});
          });
        }
      }
      // デザインが`setState`するタイミングで_globalKey.currentContext?.sizeがerrorになるので一旦、try catchでエラーを回避。
    } catch (e) {
      print(e);
    }

    return true;
  }
}

実装してみて

基本的にはViewが生成(initState)された時点でどちらを表示するかを判定し使うのがいいのかもしれない。(リアルタイムのサイズ変更でやろうとするとBuilderをしっかりやらないとダメかも)
BottomNavigationBarに関しては、ScaffoldのbottomNavigationBarに割り当てるがNavigationRailに関してはRowの中で組むという若干不思議な感じになった。将来的にはここもScaffoldにのパラメータで用意してくれたらより最高だなと思った。

今後

今はPythonでWebの情報をスクレイピングしてDBに入れて、それをAPIで返すバックエンドを作っているので、近々それも記事にできたらいいなと思います。

Discussion