LoginSignup
8
7

More than 1 year has passed since last update.

【Flutter】タブ内のボタンでページ遷移(画面遷移)

Last updated at Posted at 2020-05-28

タブ内のページ遷移するボタン

Flutterで、タブ内のボタンを押したら、新しいページ遷移される仕組みを作ってみましょう。デモページは以下をご覧ください、(上タブと下タブでは、コードの構成が少し異なります。)

上タブのデモページ
下タブのデモページ

2020-05-29 (3).png ↑下タブ版の例

上タブ… DefaultTabController(上部に設置するタブ)
下タブ… BottomNavigationBar(下部に設置するタブ)

1. まずはタブとそのページを作ってみる

上タブはflutter.dev/docsを、
下タブは、flutter.dev/flutter/material
参考に、タブとそのページのみを作成してみましょう。

2. ページ遷移を作成する

以下のURL
flutter.dev/docs を参考に、ページ遷移を作成する

3. そのまま実装すると、エラーが発生する!

上タブ版のエラー

Navigator operation requested with a context that does not include a Navigator.

このエラーメッセージは、「Navigator.pushで、Navigatorが見つからない」といっています。NavigatorオブジェクトはMaterialApp内で生成されますが、そのままのコードだと、MaterialAppのContextへ辿ることができません。
参考:Flutterの公式YouTube

下タブ版のエラー

Error: Getter not found: 'context'.

このエラーメッセージは、「ページ1の画面にはcontextがない」といっています。

解決法

上タブ版の解決策(どちらでもOK)

  • DefaultTabControllerをBuilderで囲む
    または
  • DefaultTabController全体を別Widgetに切り出す
    (今回は、2個目の切り出す方法を採用)

下タブ版の解決策

  • ページ1、ページ2それぞれを別Widgetを別クラスに切り出す

まとめ

上タブ版

main.dart
import 'package:flutter/material.dart';

// 最初に呼び出される関数(MyApp関数)
void main() {
  runApp(TabBarDemo());
}

// 全体
class TabBarDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: TabPage(),
    );
  }
}

// 2つのタブの画面
class TabPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 2,
      // Scaffold を使うことで、 appBar を利用できる
      child: Scaffold(
        appBar: AppBar(
          // appBar の bottomの部分 に作成
          bottom: TabBar(
            tabs: [
              Tab(icon: Icon(Icons.home)),
              Tab(icon: Icon(Icons.directions_walk)),
            ],
          ),
          title: Text('Demo (top)'),
        ),
        body: TabBarView(
          children: [

            // ページ1
            Center(
              child: RaisedButton(
                child: Text('ページ3へ'),
                color: Colors.orange,
                textColor: Colors.white,
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => Page3(),
                    ),
                  );
                },
              ),
            ),
            
            // ページ2
            Center(
              child: Text('ページ2'),
            ),
            
          ],
        ),
      ),
    );
  }
}

// ページ3(遷移するページ)
class Page3 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("ページ3"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text('戻る'),
        ),
      ),
    );
  }
}

下タブ版

main.dart
import 'package:flutter/material.dart';

// 最初にMyAppを実行する
void main() => runApp(MyApp());

// 初期設定
class MyApp extends StatelessWidget {
  static const String _title = 'Flutter Code Sample';
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _title,
      home: MyStatefulWidget(),
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
    );
  }
}

// 状態管理
class MyStatefulWidget extends StatefulWidget {
  MyStatefulWidget({Key key}) : super(key: key);
  @override
  // 更新
  _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  // 初期状態での選択は左のタブ
  int _selectedIndex = 0;
  
  static List<Widget> _widgetOptions = <Widget>[
    // ページ1の画面
    Page1(),
    // ページ2の画面
    Page2(),
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('DEMO (bottom)'),
      ),
      body: Center(
        child: _widgetOptions.elementAt(_selectedIndex),
      ),

      // 下のナビゲーションボタン
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            title: Text('ページ1'),
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.directions_walk),
            title: Text('ページ2'),
          ),
        ],

        // 選択をナビゲーションアイコンに反映
        currentIndex: _selectedIndex,
        // 選択したときはオレンジ色にする
        selectedItemColor: Colors.amber[800],
        // タップできるように
        onTap: _onItemTapped,
      ),
    );
  }
}

// (ページ1)左ページ
class Page1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: RaisedButton(
        child: Text('ページ3へ'),
        color: Colors.orange,
        textColor: Colors.white,
        onPressed: () {
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => NewPage(),
            ),
          );
        },
      ),
    );
  }
}

// (ページ2)右ページ
class Page2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text(
      'ページ2',
    );
  }
}


// ページ3(遷移するページ)
class NewPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("ページ3"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text('戻る'),
        ),
      ),
    );
  }
}

これでページ遷移の仕組みが完成しました!

最後に

Flutterの初学者の方々のお役に立てれば幸いです。もし誤り等ございましたら、コメントいただければと思います。

8
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7