Flutter - 2 : TabBar顶部导航

Flutter - 2 : TabBar顶部导航

接着第一部分来,这次是TabBar,这玩意简直是新闻类应用的标配。

这是方法2的图。。。当然,颜值。。要啥自行车。。

  • 方法 1 :

这个方法比较官方,然鹅,也很坑爹,因为你每滑动一次,它才会创建相应的Widget,也就是说没有预加载界面,导致在滑动的时候这个界面卡的简直不忍直视,然而好像也没有属性能设置成预加载模式。或许是我打开的方法不对?

class EditPage extends StatelessWidget {
  final int len = 8;
  final List<String> titles = ["科技", "汽车", "金融", "体育", "影视", "军事", "娱乐", "头条"];

  final TextStyle selected_style = new TextStyle(
      fontSize: 14.0, fontWeight: FontWeight.bold);
  final TextStyle unselected_style = new TextStyle(
      fontSize: 12.0, fontWeight: FontWeight.bold);

  final List<Tab> tabs = new List<Tab>();

  final List<NewsPage> news_pages = new List<NewsPage>();

  @override
  Widget build(BuildContext context) {

    createTabs();

    createTabVews();

    return new DefaultTabController(
        length: len,
        initialIndex: 0,
        child: new Column(
          children: <Widget>[
            new Container(
              color: Colors.lightBlue,
              child: new AspectRatio(
                  aspectRatio: 8.0,
                  child: new TabBar(
                      isScrollable: true,
                      indicatorColor: Colors.white,
                      indicatorWeight: 2.0,
                      labelStyle: selected_style,
                      unselectedLabelStyle: unselected_style,
                      tabs: tabs)),
            ),
            new Expanded(child: new TabBarView(children: news_pages))
          ],
        )
    );
  }

  void createTabs(){
    for(int i=0;i<len;i++){
      tabs.add(new Tab(text: titles[i],));
    }
  }

  void createTabVews(){
    for(int i=0;i<len;i++){
      news_pages.add(new NewsPage(i,null));
    }
  }
}

使用StatefulWidget的时候,需要自己创建一个TabControllerStatelessWidget可以用DefaultTabController

  • 方法 2 :

这个方法比较土,跟上一次一样,用StackOffset去实现显示与隐藏的效果。

2.1 :先弄个基础页面

先放一个列控件Column,然后用AspectRantio把导航栏高度设置为宽度的1/8,最后用Expanded把剩下的控件占满作为body的空间。顺便加个手势检测控件,可以加个横向滑动页面啥的。

class BodyPageA extends StatelessWidget {
 final int len = 8;

 @override
 Widget build(BuildContext context) {

   return new Column(
     children: <Widget>[
       new AspectRatio(
         aspectRatio: 8.0,
         child: new Container(
             color: Colors.lightBlue,
             child: new NewsTabView(len)
         ),
       ),
       new Expanded(
           child: new GestureDetector(
             child: new BodyPageStack(len),
           ))
     ],
   );
 }
}
2.2 :导航栏

连底部装饰条都没有的"返祖式"导航栏。它负责创建自己的tab子集,并在某一个tab被点击之后,通知上一个被选中的tab刷新为未被选中状态。

//   新闻tabView
class NewsTabView extends StatelessWidget {
 final int len;

 final List<Expanded> expanded_tabs = new List<Expanded>();

 int _selected_position = 0;

 NewsTabView(this.len);

 @override
 Widget build(BuildContext context) {
   createExpandedTab();
   onSelectedChanged();
   return new Row(children: expanded_tabs,);
 }

 void createExpandedTab(){
   List<String> titles = ["科技", "汽车", "金融", "体育", "影视", "军事", "娱乐", "头条"];
   for(int i=0;i<len;i++){
     bool selected = false;
     //  设置0号位为默认选择位
     if(i == 0){
       selected = true;
     }
     expanded_tabs.add(new Expanded(child: new NewsTab(i, titles[i],selected)));
   }
 }

//   导航栏点击位置的监听
 void onSelectedChanged(){
   Event.event_bus.on<PageATabChanged>().listen((PageATabChanged event){
     int position = event.message;
     print(position);
     if(_selected_position != position){
       NewsTab tab = expanded_tabs[_selected_position].child;
       tab.setUnselected();
       _selected_position = position;
     }
   });
 }
}

下面是TabView里面的Tab们,它们负责被点击之后改变自己的状态,并通知导航栏和body部分做出相应的变化。

//   导航tab
class NewsTab extends StatefulWidget{

 final int position;

 final String title;

 final bool selected;

 final TextStyle select_style = new TextStyle(color: Colors.redAccent,fontSize: 15.0,fontWeight: FontWeight.bold);
 final TextStyle unselect_style = new TextStyle(color: Colors.white,fontSize: 12.0,fontWeight: FontWeight.bold);

 _NewsTabState _state;

 NewsTab(this.position, this.title,this.selected);

 @override
 State<StatefulWidget> createState() {

   _state = new _NewsTabState(position,title,select_style,unselect_style,selected);
   return _state;
 }

 void setUnselected(){
   _state.setUnselected();
 }
}

class _NewsTabState extends State<NewsTab>{

 final int position;

 final String title;

 final TextStyle select_style;
 final TextStyle unselect_style;

 bool _selected = false;

 _NewsTabState(this.position,this.title,this.select_style,this.unselect_style,this._selected);

 @override
 Widget build(BuildContext context) {

   return new GestureDetector(
     child: new Tab(child: new Text(title,style: (_selected ? select_style : unselect_style),),),
     onTap: setSelected,
   );
 }

 void setSelected() {
   if(!_selected){
     setState(() {
       _selected = true;
       Event.event_bus.fire(new PageATabChanged(position));
     });
   }
 }

 void setUnselected(){
   setState(() {
     _selected = false;
   });
 }

 bool get selected => _selected;

 set selected(bool value) {
   _selected = value;
 }
}
2.3 Body控件

继承StatelessWidget,然后用它实现对tab点击位置的监听,然后控制新闻列表页面的显示与隐藏

//   body页面Stack
class BodyPageStack extends StatelessWidget {
  final int len;

  final List<OffstagePage> page_list = new List<OffstagePage>();

  int _current_position = 0;

  BodyPageStack(this.len);

  @override
  Widget build(BuildContext context) {

    createPageList();
    onSelectChanged();
    Stack stack = new Stack(children: page_list,);

    return stack;
  }

  //   创建statk控件列表
  void createPageList() {
    for (int i = 0; i < len; i++) {
      bool hide = true;
      if (i == _current_position) {
        hide = false;
      }
      page_list.add(new OffstagePage(hide,new NewsPage(i,null)));
    }
  }

  //  修改隐藏项
  void resetPageList(int position) {
    page_list[_current_position].onOffsetChanged(true);
    page_list[position].onOffsetChanged(false);
  }

  //   选项更改监听
  void onSelectChanged() {
    Event.event_bus.on<PageATabChanged>().listen((PageATabChanged event) {
      int position = event.message;
      print(position);
      if(_current_position != position){
        resetPageList(position);
        _current_position = position;
      }
    });
  }
}

然后是在Offstage里面放进真正的列表页面,并为其提供隐藏与显示的方法

//   可隐藏的pageye页面
class OffstagePage extends StatefulWidget{

  final bool hide;

  final NewsPage page;

  OffstagePage(this.hide,this.page);

  _OffstagePageState _state;

  @override
  State<StatefulWidget> createState() {
    _state = new _OffstagePageState(hide,page);
    return _state;
  }

  void onOffsetChanged(bool hide){
    _state.setState((){
      _state.hide = hide;
    });
  }
}

class _OffstagePageState extends State<OffstagePage>{

  bool _hide;

  final NewsPage page;

  _OffstagePageState(this._hide,this.page);

  @override
  Widget build(BuildContext context) {
    return new Offstage(
      offstage: _hide,
      child: page,
    );
  }

  bool get hide => _hide;

  set hide(bool value) {
    _hide = value;
  }
}
2.4 最后是消息类
class PageATabChanged{
  final int message;
  PageATabChanged(this.message);
}

猜你喜欢

转载自blog.csdn.net/weixin_42572156/article/details/83024146