颤振中的多标签/页面视图

时间:2017-09-04 18:36:56

标签: flutter

如何在flutter中创建多页面视图,其中页面对应于bottomnavigationbar中的选项卡,使得与页面对应的窗口小部件仅按需构建一次。

例如,考虑一个简单的Facebook应用程序类型的UI,带有两个选项卡 - 提要和通知,具有以下行为:

  1. Feed和通知都是通过网络提取的项目列表。
  2. 假设原始标签是Feed,则只有在用户点击通知标签
  3. 时才会提取通知
  4. 如果用户滚动了Feed,点击了通知图标,然后再次点击Feed图标,则应记住滚动位置。
  5. 如果我使用TabBarView,它会在每次更改选项卡时重建小部件,因此不会保留滚动位置。

2 个答案:

答案 0 :(得分:12)

要为TabBarView的页面提供唯一的滚动位置存储容器,请使用PageStorageKey

video

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  State createState() => new MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  TabController _controller;
  int _tab = 0;

  @override
  void initState() {
    _controller = new TabController(length: 2, vsync: this);
  }

  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(title: new Text('Example App')),
      body: new TabBarView(
        controller: _controller,
        children: <Widget>[
          new ListView.builder(
            key: new PageStorageKey('feed'),
            itemBuilder: (BuildContext context, int index) {
              return new ListTile(
                title: new Text('Feed Item $index'),
              );
            },
          ),
          new ListView.builder(
            key: new PageStorageKey('notifications'),
            itemBuilder: (BuildContext context, int index) {
              return new ListTile(
                title: new Text('Notification $index'),
              );
            },
          ),
        ],
      ),
      bottomNavigationBar: new BottomNavigationBar(
        onTap: (int value) {
          _controller.animateTo(value);
          setState(() {
            _tab = value;
          });
        },
        currentIndex: _tab,
        items: <BottomNavigationBarItem>[
          new BottomNavigationBarItem(
            icon: new Icon(Icons.home),
            title: new Text('Home'),
          ),
          new BottomNavigationBarItem(
            icon: new Icon(Icons.notifications),
            title: new Text('Notifications'),
          ),
        ],
      ),
    );
  }
}

答案 1 :(得分:1)

enter image description here

完整示例

首先创建一个MyBottomBarDemo类

class MyBottomBarDemo extends StatefulWidget {
  @override
  _MyBottomBarDemoState createState() => new _MyBottomBarDemoState();
}

class _MyBottomBarDemoState extends State<MyBottomBarDemo> {
  int _pageIndex = 0;
  PageController _pageController;

  List<Widget> tabPages = [
    Screen1(),
    Screen2(),
  ];

  @override
  void initState(){
    _pageController = PageController(initialPage: _pageIndex);
    super.initState();
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Multi tab / page view", style: TextStyle(color: Colors.white)),
        backgroundColor: Colors.deepPurple,
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _pageIndex,
        onTap: onTabTapped,
        backgroundColor: Colors.white,
        items: <BottomNavigationBarItem>[
          BottomNavigationBarItem( icon: Icon(Icons.home), title: Text("Home")),
          BottomNavigationBarItem(icon: Icon(Icons.mail), title: Text("Messages")),
        ],

      ),
      body: PageView(
        children: tabPages,
        onPageChanged: onPageChanged,
        controller: _pageController,
      ),
      drawer: SlideDrawer(),
    );
  }
  void onPageChanged(int page) {
    setState(() {
      this._pageIndex = page;
    });
  }

  void onTabTapped(int index) {
    this._pageController.animateToPage(index,duration: const Duration(milliseconds: 500),curve: Curves.easeInOut);
  }
}

然后创建您的寻呼机屏幕

class Screen1(), extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.yellow,
      child: StreamBuilder(
        stream: getHomeDataList(param).asStream(), // This is your function of get data from network
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          switch (snapshot.connectionState) {
            case ConnectionState.waiting: {
                return LoadingIndicatorView();
              }
            case ConnectionState.active: {
                break;
              }
            case ConnectionState.done: {
                if (snapshot.hasData) {
                  if (snapshot.data != null) {
                    if (snapshot.data.length > 0) {
                      return Container(
                        color: offBlueColor,
                        child: Scrollbar(
                          child: ListView.builder(
                              key: PageStorageKey('homeList'),
                              padding: EdgeInsets.all(5),
                              itemCount: snapshot.data.length,
                              itemBuilder: (context, index) {
                                return ListTile(
                                  title: Text('Home Page index $index')
                                );;
                              }),
                        ),
                      );
                    } else {
                      return Text("No data found");
                    }
                  } else {
                    // display error message data is null.
                    return Text("No data found");
                  }
                } else if (snapshot.hasError) {
                  return Text(snapshot.error.toString());
                } else {
                  return Text("Something went wrong");
                }
                break;
              }
            case ConnectionState.none:{
                break;
              }
          }
          return Container();
        },
      ),
    );
  }
}