質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.77%

bottomNavigationBarをキープして画面遷移したい

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 298

smilax

score 20

前提・実現したいこと

Flutterの下タブメニューを押して、画面遷移する際にNavigationBarが消えてしまわないようキープする挙動を実現したいです。
下記の記事を参考にしながら、実装しようとするもうまくいきません。
https://qiita.com/canisterism/items/d648da85c300a3751db0

開発条件としてProviderを使用することです。
ChangeNotifierからtabBuilderに対応する値をセットしてあげたいのですが、どう設定していいかがわからずtabBuilderの部分で

The argument type 'Consumer<MyPageChangeNotifier>' can't be assigned to the parameter type 'Widget Function(BuildContext, int)'.

とエラーがでてしまいます。



import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';

import 'MyPageChangeNotifier.dart';
import 'package:talent/view/screen/myPage/component/Profile.dart';
import 'package:talent/view/screen/myPage/component/RequestStatus.dart';
import 'package:talent/view/component/Header.dart';
import 'package:talent/PresentIcon.dart';
import 'package:talent/SettingIcon.dart';
import 'package:talent/view/screen/myPage/component/NoticeItem.dart';
// import 'package:talent/view/component/BorderedText.dart';

class MyPageScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<MyPageChangeNotifier>(
      create: (context) => MyPageChangeNotifier(),
      child:  CupertinoTabScaffold(
        tabBar: CupertinoTabBar(
        items: <BottomNavigationBarItem>[
        BottomNavigationBarItem(icon: Icon(Icons.face), title: Text('タレント')),
        BottomNavigationBarItem(icon: Icon(PresentIcon.present_icon), title: Text('リクエスト')),
        BottomNavigationBarItem(icon: Icon(SettingIcon.setting_icon), title: Text('設定')),
      ],
      currentIndex: currentIndex,
      onTap: (n) {  
      switch (n) {
          case 0:
            onTapItem(TabItem.Talent);
            currentIndex = TabItem.Talent.index;
            break;
          case 1:
            onTapItem(TabItem.Message);
            currentIndex = TabItem.Message.index;
            break;
          case 2:
            onTapItem(TabItem.Setting);
            currentIndex = TabItem.Setting.index;
            break;
        }
      }
      ),
      tabBuilder: Consumer<MyPageChangeNotifier>(builder: (context, value, child) {
          value.navigation(context, tabItem)
          });  //エラー箇所
        body: Consumer<MyPageChangeNotifier>(
          builder: (context, value, child) {
            return SingleChildScrollView(
              child: Column(
                children: <Widget>[
                  Container(
                    color: Color(0xffF5F6F9),
                    child: Padding(
                      padding: EdgeInsets.only(top: 40.0, bottom: 60.0, right: 40.0, left: 40.0),
                      child: Column(
                        children: <Widget>[
                          Profile(),
                          SizedBox(height: 20.0),
                          RequestStatus(),
                        ],
                      ),
                    ),
                  ),
                  Padding(
                    padding: EdgeInsets.only(top: 40.0, right: 24.0, left: 24.0, bottom: 63.0),
                    child: Column(children: <Widget>[
                      // BorderedText(text: "お知らせ"),
                      SizedBox(height: 24.0),
                      NoticeItem(text: value.text, date: value.date),
                      SizedBox(height: 20.0),
                      NoticeItem(text: value.text, date: value.date),
                    ]),
                  ),
                ],
              ),
            );
          },
        ),
      )
    );
  }
}

Provider 

import 'package:flutter/cupertino.dart';
import 'package:talent/view/screen/myPage/MyPageScreen.dart';

enum TabItem {
  Talent,
  Message,
  Setting,
}

class MyPageChangeNotifier with ChangeNotifier {
  String _date = "2020.XX.XX";
  String _text = "お知らせお知らせお知らせお知らせお知らせお知らせお知らせお知らせお知らせお知らせお知らせお知らせ。";

  get date => _date;
  get text => _text;

  Widget (TabItem tabItem) {
    switch (tabItem) {
      case TabItem.Talent: // 1番左のタブが選ばれた時の画面
        return CupertinoTabView(builder: (context) {
          return CupertinoPageScaffold(
            child: MyPageScreen(), // 表示したい画面のWidget
          );
        });
        break;
      case TabItem.Message: // 1番左のタブが選ばれた時の画面
        CupertinoTabView(builder: (context) {
          return CupertinoPageScaffold(
            child: MyPageScreen(), // 表示したい画面のWidget
          );
        });
        break;
      case TabItem.Setting: // 1番左のタブが選ばれた時の画面
        CupertinoTabView(builder: (context) {
          return CupertinoPageScaffold(
            child: MyPageScreen(), // 表示したい画面のWidget
          );
        });
        break;
    }
    return null;
  }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

(追記)
https://qiita.com/canisterism/items/d648da85c300a3751db0
の内容を読まずに最初の回答をしましたが

Navigator.of(context).pushを行ったときに消える動作を防ぎたいということでしたら、まず、上記URLにもあるとおりですが、とくになにもせずにNavigator.of(context)を行うと最も上位のWidgetが置き換わってしまうためにNaviBarが消えてしまいます。

なので、「常にNaviBarを表示しておきたい」という場合には上記例のtabBuilderにNavigatorを含んでおけばよいということになります。

ただし、CupertinoTabScaffoldではTab間の移動には基本的にNavigator.of(context)を使わないように思うので、これはあくまで「別のページを表示する」ときにも「常に元のページのNaviBarを表示しておきたい」ときの操作だと考えます。使用シーンがイマイチわかりませんが、一応、簡単に書けるので、設定のタブから別のページへの遷移をもとのページのTabを維持したまま行う例を以下に示します。

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key key}) : super(key: key);
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: Scaffold(
          body: ChangeNotifierProvider<_NavigatorHistoryStore>(
              create: (_) => _NavigatorHistoryStore(),
              child: const MainPage())),
    );
  }
}

const int talentTabIndex = 0;
const int requestTabIndex = 1;
const int settingTabIndex = 2;

class MainPage extends StatelessWidget {
  const MainPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) => Consumer<_NavigatorHistoryStore>(
      builder: (BuildContext context, _NavigatorHistoryStore navi, _) =>
          CupertinoTabScaffold(
              controller: navi.controller,
              tabBar: CupertinoTabBar(items: const <BottomNavigationBarItem>[
                BottomNavigationBarItem(
                    icon: Icon(Icons.face), title: Text('タレント')),
                BottomNavigationBarItem(
                    icon: Icon(Icons.mail), title: Text('リクエスト')),
                BottomNavigationBarItem(
                    icon: Icon(Icons.settings), title: Text('設定')),
              ]),
              tabBuilder: (BuildContext context, int index) => Navigator(
                  initialRoute: 'main/tabs',
                  onGenerateRoute: (RouteSettings settings) =>
                      MaterialPageRoute<PageRoute<Widget>>(
                          settings: settings,
                          builder: (_) => const <Widget>[
                                _TalentTabPage(),
                                _RequestTabPage(),
                                _SettingTabPage(),
                              ][index]))));
}

class _TalentTabPage extends StatelessWidget {
  const _TalentTabPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) => Consumer<_NavigatorHistoryStore>(
      builder: (BuildContext context, _NavigatorHistoryStore navi, _) => Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text('タレントのタブです'),
                RaisedButton(
                    child: const Text('リクエストを表示'),
                    onPressed: () => navi.moveTo(requestTabIndex)),
                RaisedButton(
                    child: const Text('設定を表示'),
                    onPressed: () => navi.moveTo(settingTabIndex)),
                RaisedButton(
                    child: const Text('もどる'),
                    onPressed: navi.hasHistory ? navi.pop : null),
              ]));
}

class _RequestTabPage extends StatelessWidget {
  const _RequestTabPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) => Consumer<_NavigatorHistoryStore>(
      builder: (BuildContext context, _NavigatorHistoryStore navi, _) => Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text('リクエストのタブです'),
                RaisedButton(
                    child: const Text('タレントを表示'),
                    onPressed: () => navi.moveTo(talentTabIndex)),
                RaisedButton(
                    child: const Text('設定を表示'),
                    onPressed: () => navi.moveTo(settingTabIndex)),
                RaisedButton(
                    child: const Text('もどる'),
                    onPressed: navi.hasHistory ? navi.pop : null),
              ]));
}

class _SettingTabPage extends StatelessWidget {
  const _SettingTabPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) => Consumer<_NavigatorHistoryStore>(
      builder: (BuildContext context, _NavigatorHistoryStore navi, _) => Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text('設定のタブです'),
                RaisedButton(
                    child: const Text('別のページを開く'),
                    onPressed: () => Navigator.of(context).push(
                        MaterialPageRoute<PageRoute<Widget>>(
                            builder: (_) => _OtherPage()))),
                RaisedButton(
                    child: const Text('もどる'),
                    onPressed: navi.hasHistory ? navi.pop : null),
              ]));
}

class _NavigatorHistoryStore extends ChangeNotifier {
  _NavigatorHistoryStore() {
    controller.addListener(push);
  }
  final CupertinoTabController controller = CupertinoTabController();

  int _prevIndex = 0;
  bool _onPop = false;
  List<int> histories = <int>[];
  bool get hasHistory => histories.isNotEmpty;

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  void moveTo(int index) {
    if (controller.index == index) {
      return;
    }
    controller.index = index;
  }

  void push() {
    if (_prevIndex == controller.index) {
      return;
    }
    if (_onPop) {
      _onPop = false;
    } else {
      histories.add(_prevIndex);
    }
    _prevIndex = controller.index;
    notifyListeners();
  }

  void pop() {
    _onPop = true;
    if (histories.isNotEmpty) {
      controller.index = histories.removeLast();
    }
  }
}

class _OtherPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) => Scaffold(
          body: Center(
              child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
            const Text('これば別のページ'),
            RaisedButton(
                child: const Text('もとのページに戻る'),
                onPressed: () => Navigator.of(context).pop()),
          ])));
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

ワンファイルで示せるように極力小さなコード例を示します。

質問の「Flutterの下タブメニューを押して、画面遷移する際にNavigationBarが消えてしまわないようキープする挙動を実現したいです」ということであれば、以下のようにすれば大丈夫です。

この目的のためにProviderを使う必要はありません。

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key key}):super(key:key);
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: const Scaffold(body: MainPage()),
    );
  }
}

class MainPage extends StatelessWidget {
  const MainPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) => CupertinoTabScaffold(
      tabBar: CupertinoTabBar(items: const <BottomNavigationBarItem>[
        BottomNavigationBarItem(icon: Icon(Icons.face), title: Text('タレント')),
        BottomNavigationBarItem(icon: Icon(Icons.mail), title: Text('リクエスト')),
        BottomNavigationBarItem(icon: Icon(Icons.settings), title: Text('設定')),
      ]),
      tabBuilder: (BuildContext context, int index) => const <Widget>[
            _TalentTabPage(),
            _RequestTabPage(),
            _SettingTabPage(),
          ][index]);
}

class _TalentTabPage extends StatelessWidget {
  const _TalentTabPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) => const Center(child: Text('タレントのタブです'));
}

class _RequestTabPage extends StatelessWidget {
  const _RequestTabPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) => const Center(child: Text('リクエストのタブです'));
}

class _SettingTabPage extends StatelessWidget {
  const _SettingTabPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) => const Center(child: Text('設定のタブです'));
}

CupertinoTabScaffoldが色々と世話を焼いてくれるので、ProviderなどによるcurrentIndexの管理は不要です。

ただし、履歴を保管したい場合やを他のWidgetからNaviを操作したい場合などには以下のようにcontrollerを持つProviderを提供することもあります。

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key key}) : super(key: key);
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: Scaffold(
          body: ChangeNotifierProvider<_NavigatorHistoryStore>(
              create: (_) => _NavigatorHistoryStore(),
              child: const MainPage())),
    );
  }
}

const int talentTabIndex = 0;
const int requestTabIndex = 1;
const int settingTabIndex = 2;

class MainPage extends StatelessWidget {
  const MainPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) => Consumer<_NavigatorHistoryStore>(
      builder: (BuildContext context, _NavigatorHistoryStore navi, _) =>
          CupertinoTabScaffold(
              controller: navi.controller,
              tabBar: CupertinoTabBar(items: const <BottomNavigationBarItem>[
                BottomNavigationBarItem(
                    icon: Icon(Icons.face), title: Text('タレント')),
                BottomNavigationBarItem(
                    icon: Icon(Icons.mail), title: Text('リクエスト')),
                BottomNavigationBarItem(
                    icon: Icon(Icons.settings), title: Text('設定')),
              ]),
              tabBuilder: (BuildContext context, int index) => const <Widget>[
                    _TalentTabPage(),
                    _RequestTabPage(),
                    _SettingTabPage(),
                  ][index]));
}

class _TalentTabPage extends StatelessWidget {
  const _TalentTabPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) => Consumer<_NavigatorHistoryStore>(
      builder: (BuildContext context, _NavigatorHistoryStore navi, _) => Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text('タレントのタブです'),
                RaisedButton(
                    child: const Text('リクエストを表示'),
                    onPressed: () => navi.moveTo(requestTabIndex)),
                RaisedButton(
                    child: const Text('設定を表示'),
                    onPressed: () => navi.moveTo(settingTabIndex)),
                RaisedButton(
                    child: const Text('もどる'),
                    onPressed: navi.hasHistory ? navi.pop : null),
              ]));
}

class _RequestTabPage extends StatelessWidget {
  const _RequestTabPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) => Consumer<_NavigatorHistoryStore>(
      builder: (BuildContext context, _NavigatorHistoryStore navi, _) => Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text('リクエストのタブです'),
                RaisedButton(
                    child: const Text('タレントを表示'),
                    onPressed: () => navi.moveTo(talentTabIndex)),
                RaisedButton(
                    child: const Text('設定を表示'),
                    onPressed: () => navi.moveTo(settingTabIndex)),
                RaisedButton(
                    child: const Text('もどる'),
                    onPressed: navi.hasHistory ? navi.pop : null),
              ]));
}

class _SettingTabPage extends StatelessWidget {
  const _SettingTabPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) => Consumer<_NavigatorHistoryStore>(
      builder: (BuildContext context, _NavigatorHistoryStore navi, _) => Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text('設定のタブです'),
                RaisedButton(
                    child: const Text('もどる'),
                    onPressed: navi.hasHistory ? navi.pop : null),
              ]));
}

class _NavigatorHistoryStore extends ChangeNotifier {
  _NavigatorHistoryStore() {
    controller.addListener(push);
  }
  final CupertinoTabController controller = CupertinoTabController();

  int _prevIndex = 0;
  bool _onPop = false;
  List<int> histories = <int>[];
  bool get hasHistory => histories.isNotEmpty;

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  void moveTo(int index) {
    if (controller.index == index) {
      return;
    }
    controller.index = index;
  }

  void push() {
    if (_prevIndex == controller.index) {
      return;
    }
    if (_onPop) {
      _onPop = false;
    } else {
      histories.add(_prevIndex);
    }
    _prevIndex = controller.index;
    notifyListeners();
  }

  void pop() {
    _onPop = true;
    if (histories.isNotEmpty) {
      controller.index = histories.removeLast();
    }
  }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.77%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る