見出し画像

Flutter生活2日目 画面遷移とタブ

どうもFlutter生活2日目のファームノート菅原です。
本日はアプリらしくなる様に画面遷移を作ってみました。

データ周りをちゃんとSingletonにする

factoryとかあるっぽいんですがいまいち、よくわからなかったので昔からの伝統的な描き方してみてます。

import 'package:scoped_model/scoped_model.dart';
class RoutingModel extends Model {
  static final RoutingModel _instance = RoutingModel();

  static RoutingModel get instance {
    return _instance;
  }

  int _tabIndex = 0;

  int get tabIndex => _tabIndex;
  void setTabIndex(int index) {
    // First, increment the counter
    _tabIndex = index;

    // Then notify all the listeners.
    notifyListeners();
  }
}

とりあえず画面の用意

画面実装だるいのでデザイン画像を貼り付けて画面を作ります。
この辺を参考に画像をコードから扱えるようにします。


画面に貼り付けてみる

ちょっと長い画面だったりとかでスクロールちゃいたくなったりしたので、公式リファレンスのWidget Categoryから適当にそれっぽいコンポーネントを漁って、SingleChildScrollViewを発見したのでおもむろに組み込む

@override
Widget build(BuildContext context) {
    return SingleChildScrollView(
        child: Image.asset('assets/DashBoard.png'),
    );
}

https://docs.flutter.io/flutter/widgets/SingleChildScrollView-class.html

タブコンテンツとして作った画面を配置

昨日作ったタブのコンテンツに当ててみる。
RoutingModelのSingleton化が光ります。
Storeのバケツリレーがなくなりました。
あと、なんかノッチ周りのマージンが面倒だったので、SafeAreaコンポーネントをさして適当にごまかしています。

// main.dart
Widget build(BuildContext context) {
    return ScopedModel<RoutingModel>(
        model: RoutingModel.instance,
        child: MaterialApp(
            theme: ThemeData(
              primarySwatch: Colors.green,
              brightness: Brightness.dark,
            ),
            home: SafeArea(
              child: Scaffold(
                body: ScopedModelDescendant<RoutingModel>(
                    builder: (context, child, model) {
                      if (model.tabIndex == 0) {
                        return DashBoard();
                      }
                      else if(model.tabIndex == 1) {
                        return Search();
                      }
                      else {
                        return NotificationView();
                      }
                    }
                ),
                bottomNavigationBar:BottomTab(),
              )
          )
        )
    );

下タブの実装ですね。
ModelをSingletonで叩いてる以外は大した事ないですね。

// BottomTab.dart

import 'package:flutter/material.dart';
import 'package:love_letter/store/RoutingModel.dart';
import 'package:scoped_model/scoped_model.dart';

class BottomTab extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return ScopedModel<RoutingModel>(
      model: RoutingModel.instance,
      child: ScopedModelDescendant<RoutingModel>(
          builder: (context, child, model) =>
              BottomNavigationBar(
                currentIndex: model.tabIndex,
                onTap: (int page) => RoutingModel.instance.setTabIndex(page),
                items: [
                  BottomNavigationBarItem(
                    icon: new Icon(Icons.home),
                    title: new Text('DashBoard'),
                  ),
                  BottomNavigationBarItem(
                    icon: new Icon(Icons.search),
                    title: new Text('Search'),
                  ),
                  BottomNavigationBarItem(
                      icon: Icon(Icons.notifications),
                      title: Text('Notification')
                  )
                ],
              )
      ),
    );
  }
}

上タブ切り替えしたくなった

というわけでやりました。
AppBarって結構カスタムできるんですね。
タイトルにアイコンつけたいなーとか思ってたんですが、AppBarのtitleってWidgetなので、なんでもぶっこめるんですね。
bottomという引数でAppbarの下に要素を混ぜれれるんですな。

import 'package:flutter/material.dart';

class DashBoard extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
        length: 2,
        child: Scaffold(
          appBar: AppBar(
            backgroundColor: Colors.transparent,
            elevation: 0,
            // titleにアイコン領域をぶっこんでみている
            title: Row(
              children: <Widget>[
                Container(
                  color: Colors.green,
                  constraints: BoxConstraints(
                    maxHeight: 30.0,
                    maxWidth: 30.0,
                    minWidth: 15.0,
                    minHeight: 15.0
                  ),
                ),
                Text('帯広牧場')
              ],
            ),
            bottom: TabBar(
              tabs: [
                Tab(text: "Home",),
                Tab(text: "Chew"),
              ],
            )
          )
          ,
          body: TabBarView(
            children: [
              SingleChildScrollView(
                child: Image.asset('assets/DashBoard.png'),
              ),
              Center(
                child: Text('page2')
              ),
            ],
          ),
        )
    );
//    return SingleChildScrollView(
//      child: Image.asset('assets/DashBoard.png'),
//    );
  }
}

普通にRouterで処理する画面遷移を作ってみる

とりあえず適当な画面

import 'package:flutter/material.dart';
import 'package:love_letter/components/template/CowDetail.dart';

class NotificationView extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Center(
      child: RaisedButton(
          child: Text('Notification'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => CowDetail()),
            );
          }
      ),
    );
  }
}

飛び先を作ってみる

import 'package:flutter/material.dart';

class CowDetail extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Screen"),
      ),
      body: Center(
        child: RaisedButton(
            onPressed: () {
              Navigator.pop(context);
            },
            child: Text('Go back!'),
        ),
      ),
    );
  }
}

だいたい、これでタブでの遷移とRouter.push,pop的な導線を作ることができました。

だんだんそれっぽくなって来ました。

ファームノートでインタラクションデザイナーやってます。 最近はFigmaとFlutterでアプリを作っています。