【问题标题】:Flutter persistent app bar across PageView跨 PageView Flutter 持久化应用栏
【发布时间】:2018-12-08 05:35:52
【问题描述】:

理想情况下,我想如下设置我的 Flutter 应用程序

  • PageView 在 3 个页面之间向左/向右滑动,底部导航栏用作标签并帮助导航
  • 顶部的持久应用栏,带有抽屉和上下文图标
  • 中间的页面内容

从图片中可以看出,我主要按照以下方式进行设置

main.dart - app entry point, set up appbar, set up pageview with children for new PeoplePage, new TimelinePage, new StatsPage

people_page.dart
timeline_page.dart
stats_page.dart

These three pages just deliver the content to the PageView children as required.

这是实现这一目标的正确方法吗?从表面上看,它工作正常。我遇到的问题是,在人员页面上,我想实现一个可选择的列表来更改应用栏标题/颜色as in this example,但应用栏设置在主页上。我可以全局访问应用栏吗?

我可以为每个页面构建一个新的 appbar,但我不想在切换页面时刷入一个新的 appbar。我更希望 appbar 看起来持久且只有内容滑入。

任何有关实现此目的的最佳方法的指导将不胜感激。

【问题讨论】:

    标签: flutter


    【解决方案1】:

    我整理了一个简单的示例,说明您如何从屏幕到页面进行通信,然后再返回。这应该可以解决您的问题。

    https://gist.github.com/slightfoot/464fc225b9041c2d66ec8ab36fbdb935

    import 'package:flutter/material.dart';
    
    void main() => runApp(TestApp());
    
    class TestApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          theme: ThemeData(
            primaryColor: Colors.green[900],
            scaffoldBackgroundColor: Colors.grey[200],
          ),
          home: MainScreen(),
        );
      }
    }
    
    class AppBarParams {
      final Widget title;
      final List<Widget> actions;
      final Color backgroundColor;
    
      AppBarParams({
        this.title,
        this.actions,
        this.backgroundColor,
      });
    } 
    
    class MainScreen extends StatefulWidget {
      final int initialPage;
    
      const MainScreen({
        Key key,
        this.initialPage = 0,
      }) : super(key: key);
    
      @override
      MainScreenState createState() => MainScreenState();
    
      static MainScreenState of(BuildContext context) {
        return context.ancestorStateOfType(TypeMatcher<MainScreenState>());
      }
    }
    
    class MainScreenState extends State<MainScreen> {
      final List<GlobalKey<MainPageStateMixin>> _pageKeys = [
        GlobalKey(),
        GlobalKey(),
        GlobalKey(),
      ];
    
      PageController _pageController;
      AppBarParams _params;
      int _page;
    
      set params(AppBarParams value) {
        setState(() => _params = value);
      }
    
      @override
      void initState() {
        super.initState();
        _page = widget.initialPage ?? 0;
        _pageController = PageController(initialPage: _page);
        WidgetsBinding.instance.addPostFrameCallback((_) {
          _pageKeys[0].currentState.onPageVisible();
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: _params?.title,
            actions: _params?.actions,
            backgroundColor: _params?.backgroundColor,
          ),
          body: PageView(
            controller: _pageController,
            onPageChanged: _onPageChanged,
            children: <Widget>[
              PeoplePage(key: _pageKeys[0]),
              TimelinePage(key: _pageKeys[1]),
              StatsPage(key: _pageKeys[2]),
            ],
          ),
          bottomNavigationBar: BottomNavigationBar(
            currentIndex: _page,
            onTap: _onBottomNavItemPressed,
            items: const <BottomNavigationBarItem>[
              BottomNavigationBarItem(
                title: Text('people'),
                icon: Icon(Icons.people),
              ),
              BottomNavigationBarItem(
                title: Text('timeline'),
                icon: Icon(Icons.history),
              ),
              BottomNavigationBarItem(
                title: Text('stats'),
                icon: Icon(Icons.pie_chart),
              ),
            ],
          ),
        );
      }
    
      @override
      void reassemble() {
        super.reassemble();
        _onPageChanged(_page);
      }
    
      void _onPageChanged(int page) {
        setState(() => _page = page);
        _pageKeys[_page].currentState.onPageVisible();
      }
    
      void _onBottomNavItemPressed(int index) {
        setState(() => _page = index);
        _pageController.animateToPage(
          index,
          duration: Duration(milliseconds: 400),
          curve: Curves.fastOutSlowIn,
        );
      }
    }
    
    abstract class MainPageStateMixin<T extends StatefulWidget> extends State<T> {
      void onPageVisible();
    }
    
    class PeoplePage extends StatefulWidget {
      const PeoplePage({Key key}) : super(key: key);
    
      @override
      PeoplePageState createState() => PeoplePageState();
    }
    
    class PeoplePageState extends State<PeoplePage> with MainPageStateMixin {
      final List<Color> _colors = [
        Colors.orange,
        Colors.purple,
        Colors.green,
      ];
    
      int _personCount = 3;
    
      @override
      void onPageVisible() {
        MainScreen.of(context).params = AppBarParams(
          title: Text('People'),
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.person_add),
              onPressed: () => setState(() => _personCount++),
            ),
          ],
          backgroundColor: Colors.green,
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return ListView.builder(
          itemCount: _personCount,
          itemBuilder: (BuildContext context, int index) {
            return Card(
              child: InkWell(
                onTap: () => _onTapCard(index),
                child: Padding(
                  padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
                  child: Row(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Material(
                        type: MaterialType.circle,
                        color: _colors[index % _colors.length],
                        child: Container(
                          width: 48.0,
                          height: 48.0,
                          alignment: Alignment.center,
                          child: Text('$index', style: TextStyle(color: Colors.white)),
                        ),
                      ),
                      SizedBox(width: 16.0),
                      Text(
                        'Item #$index',
                        style: TextStyle(
                          color: Colors.grey[600],
                          fontSize: 18.0,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            );
          },
        );
      }
    
      void _onTapCard(int index) {
        Scaffold.of(context).showSnackBar(SnackBar(content: Text('Item #$index')));
      }
    }
    
    class TimelinePage extends StatefulWidget {
      const TimelinePage({Key key}) : super(key: key);
    
      @override
      TimelinePageState createState() => TimelinePageState();
    }
    
    class TimelinePageState extends State<TimelinePage> with MainPageStateMixin {
      @override
      void onPageVisible() {
        MainScreen.of(context).params = AppBarParams(
          title: Text('Timeline'),
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.alarm_add),
              onPressed: () {},
            ),
          ],
          backgroundColor: Colors.purple,
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: Text('Coming soon'),
        );
      }
    }
    
    class StatsPage extends StatefulWidget {
      const StatsPage({Key key}) : super(key: key);
    
      @override
      StatsPageState createState() => StatsPageState();
    }
    
    class StatsPageState extends State<StatsPage> with MainPageStateMixin {
      @override
      void onPageVisible() {
        MainScreen.of(context).params = AppBarParams(
          title: Text('Stats'),
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.add_box),
              onPressed: () {},
            ),
          ],
          backgroundColor: Colors.orange,
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: Text('Coming soon'),
        );
      }
    }
    

    【讨论】:

    • @BGH 我想我收到了一条通知,询问是否可以将其分解为多个文件。但我现在看不到。
    【解决方案2】:

    解决此问题的一种方法是将 AppBar 标题和背景颜色作为状态变量,并在您的 PageView 中将 onPageChanged 设置为一个函数。此函数接受页面 int 并基于页面 int 将标题和颜色的状态设置为您想要的值。对于多选列表,您将标题设置为保留您选择的值的变量,可以将其保留为主页中的状态变量并将其传递给子组件。您可以使用任何状态管理策略,这应该可以正常工作。

    onPageChanged 函数示例:

    void onPageChanged(int page) {
        String _temptitle = "";
        Color _tempColor;
        switch (page) {
          case 0:
            _temptitle = "People";
            _tempColor = Colors.pink;
            break;
          case 1:
            _temptitle = "Timeline";
            _tempColor = Colors.green;
            break;
          case 2:
            _temptitle = "Stats";
            _tempColor = Colors.deepPurple;
            break;
        }
        setState(() {
          this._page = page;
          this._title = _temptitle;
          this._appBarColor = _tempColor;
        });
      }
    

    所以对于多选的情况,不是将标题设置为某个常量,而是将标题设置为保存所选选项值的变量。

    完整代码在这里:

    import 'package:flutter/material.dart';
    
    void main() => runApp(new MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Flutter Demo',
          theme: new ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: new MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => new _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      PageController _pageController;
      int _page = 0;
      String _title = "MyApp";
      Color _appBarColor = Colors.pink;
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(
            title: new Text(_title),
            backgroundColor: _appBarColor,
          ),
          body: PageView(
            children: <Widget>[
              Container(
                child: Center(child: Text("People")),
              ),
              Container(
                child: Center(child: Text("Timeline")),
              ),
              Container(
                child: Center(child: Text("Stats")),
              ),
            ],
            controller: _pageController,
            onPageChanged: onPageChanged,
          ),
          bottomNavigationBar: BottomNavigationBar(
            items: [
              BottomNavigationBarItem(
                icon: Icon(Icons.people),
                title: Text("People"),
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.access_time),
                title: Text("Timeline"),
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.pie_chart),
                title: Text("Stats"),
              ),
            ],
            onTap: navigateToPage,
            currentIndex: _page,
          ),
        );
      }
    
      void navigateToPage(int page) {
        _pageController.animateToPage(page,
            duration: Duration(milliseconds: 300), curve: Curves.ease);
      }
    
      void onPageChanged(int page) {
        String _temptitle = "";
        Color _tempColor;
        switch (page) {
          case 0:
            _temptitle = "People";
            _tempColor = Colors.pink;
            break;
          case 1:
            _temptitle = "Timeline";
            _tempColor = Colors.green;
            break;
          case 2:
            _temptitle = "Stats";
            _tempColor = Colors.deepPurple;
            break;
        }
        setState(() {
          this._page = page;
          this._title = _temptitle;
          this._appBarColor = _tempColor;
        });
      }
    
      @override
      void initState() {
        super.initState();
        _pageController = new PageController();
        _title = "People";
      }
    
      @override
      void dispose() {
        super.dispose();
        _pageController.dispose();
      }
    }
    

    您可以根据自己的需要改进此代码。希望这在某种程度上有所帮助。让我知道这个答案是否有什么我可以改进的地方。

    【讨论】:

    • 没关系,但我可以更改应用栏标题 a) 如果页面内容存储在不同的 .dart 文件中,更重要的是 b) 我想更改不在页面上的应用标题已更改,但发生在网页浏览内容中的事件(例如单击列表项)。
    • 应该可以,我用Inherited Widget做了一个懒惰的实现,有点用。您可以尝试进一步改进它,我添加了一个存储在不同 dart 中的小部件,并且我正在访问继承的状态并对其进行修改。唯一的问题是它不会立即更新。我现在在做我的日常工作,所以无法尝试让它变得更好,但你可以尝试调查一下。 github.com/sudhanshu-15/stackoverflowanser
    • 我在 github 上对我的代码进行了一些更改,我正在将回调传递给子小部件并使用标题上的它设置状态,到目前为止它似乎正在工作并解决了你的问题.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-17
    • 2017-11-27
    • 2020-09-03
    • 2019-03-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多