首页 > 解决方案 > 跨 PageView Flutter 持久化应用栏

问题描述

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

如图所示,我主要按照以下方式设置了我想要的方式

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.

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

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

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

样本

标签: flutter

解决方案


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

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'),
    );
  }
}

推荐阅读