Flower_gift 简单的Flutter实战app(四)

0001.jpg
项目git地址:flower_gift
tabBar-分类界面:

效果:


11324.gif
头部搜索栏的搭建:
//顶部搜索框;
  Widget _buildSearchBar(){
    return Container(
              margin: EdgeInsets.symmetric(horizontal: 10.0,vertical: 8.0),
              decoration: BoxDecoration(
                 color: Colors.black12,
                 borderRadius: BorderRadius.all(Radius.circular(22.0)),
              ),
              child: Row(
                 crossAxisAlignment: CrossAxisAlignment.start,
                 children: <Widget>[
                    Expanded(
                      flex: 1,
                      child: Container(
                        padding: EdgeInsets.symmetric(horizontal: 8.0),
                        child: TextFormField(
                            decoration: InputDecoration(
                                border: InputBorder.none,
                                hintText: "请输入关键字",
                                hintStyle: TextStyle(
                                  color: Colors.black
                                ),
                                icon: Icon(Icons.search,color:Colors.black)
                            ),
                          ),
                      ) 
                      
                    ),
                    
                 ],
              ),
    );
  }

使用:

 return Scaffold(
        appBar: AppBar(
           backgroundColor: Colors.white,
           title: _buildSearchBar()
        ),
        body: ...

这里就是说,开始我以为这个Scaffold的title属性只能是这个Text组件控制这个导航栏title.原来这个也是完全可以自定义的。

body布局部分:
 body: FutureBuilder(
            future: _getCategoryPage(context),
            builder: (context,snapshot){
              if(snapshot.hasData){
                return Container(
                    color: Colors.white,
                    child: Row(
                        children: <Widget>[
                          Expanded(
                            flex: 2,
                            child: CategoryLeftNav()
                          ),
                          Expanded(
                            flex: 5,
                            child: CategoryRightCategory()
                          )
                        ],
                      ),  
                );  
              }else{
                return Center(
                  child: Text("暂无数据"),
                );
              }
            },
        ),


 
  //网络请求
   Future _getCategoryPage(BuildContext context) async{
     await Provide.value<CategoryPageProvide>(context).getCategoryPageData();
     return "完成加载....";
  }

整体布局我把这个作为左右两部分吧,所以这个就直接用Row,然后里面嵌套这个Expanded组件,用flex来控制所占宽度比。

左边导航栏
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../model/categoryPage_model_entity.dart';
import 'package:provide/provide.dart';
import '../../provide/categoryPage_provide.dart';

class CategoryLeftNav extends StatelessWidget {
  
  @override
  Widget build(BuildContext context) {
    return Provide<CategoryPageProvide>(
        builder: (context,child,val){
          var categoryPageModelEntity = val.categoryModelEntity;
          return  Container(
            decoration: BoxDecoration(
              border: Border(
                right: BorderSide(
                    color: Colors.black12,
                    width: 1.0,
                    style: BorderStyle.solid
                )
              )
            ),
            child: ListView.builder(
              itemCount: categoryPageModelEntity.categorys.length,
              itemBuilder:(context,index){
                  return Container(
                      color: Colors.white,
                      child: InkWell(
                          //设置点击闪烁那一下的文字颜色
                          splashColor: Colors.redAccent.withOpacity(0.5),
                          onTap: (){
                             Provide.value<CategoryPageProvide>(context).switchCategory(index);
                          },
                          child: Column(
                            children: <Widget>[
                              SizedBox(height: 15.0,),
                                Container(
                                  alignment: Alignment.center,
                                  padding: const EdgeInsets.symmetric(vertical: 4.0),
                                  decoration: (Provide.value<CategoryPageProvide>(context).currentIndex == index) ?  BoxDecoration(
                                      border: Border(
                                          left: BorderSide(
                                            color: Colors.orange,
                                            width: 3.0,
                                            style: BorderStyle.solid
                                          )
                                      )
                                    ) : null,
                                    child: Text(
                                        categoryPageModelEntity.categorys[index].type,
                                        style: TextStyle(fontSize: 18.0),
                                      ),
                              ),
                              SizedBox(height: 15.0,),
                              
                            ],
                          ) 
                        
                          
                      )
                      
                  );
              },
            ),
            
        );
      }
   );
  }
}

这个布局就比较简单的,主要就是利用Provide来管理了当前点击的分类项,然后左边的指示条我就直接用的这个边框来做的。

右边分类布局部分:
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../model/categoryPage_model_entity.dart';
import 'package:provide/provide.dart';
import '../../provide/categoryPage_provide.dart';

class CategoryRightCategory extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provide<CategoryPageProvide>(
        builder: (context,child,val){
           var currentIndex = Provide.value<CategoryPageProvide>(context).currentIndex;
           var categoryModelCategory = val.categoryModelEntity.categorys[currentIndex];
           var categoryBanners = categoryModelCategory.categoryBanner;
           var areasItem = categoryModelCategory.areas[0];//默认只做第一排的商品分类详情了
           var head = areasItem.head;//看看是否有头部标题栏
           var contents = areasItem.contents;
           return Container(
              child:ListView(
                
                   children: <Widget>[
                      _buildTopBanner(context, categoryModelCategory.banner),
                    (categoryBanners != null) ? _buildCategoryBanner(context, categoryBanners) : SizedBox(height: 1.0,),
                    (head != null) ? _buildTitle(head) : SizedBox(height: 1.0,),
                    Divider(),
                    _buildCategoryShow(context, contents),
                   ],
                 
              )
             
           );
        },
    );
    
  }

  //顶部banner
  Widget _buildTopBanner(BuildContext context,CategoryModelCategorysBanner banner){
      return Padding(
         padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
         child: Container(
            height: MediaQuery.of(context).size.height / 7,
            child: Image.network(banner.imageUrl,fit:BoxFit.fill,),
         ), 
      );
   }

   //分类banner -- 需要判断显示否
   Widget _buildCategoryBanner(BuildContext context,List<CategoryModelCategorysCategorybanner> categoryBanners){
      return Padding(
        padding: const EdgeInsets.symmetric(horizontal: 10.0),
        child: Container(
           height: MediaQuery.of(context).size.height / 7,
           child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: _categoryBanners(context, categoryBanners)
           ),
        ),
      );
   }
   List<Widget> _categoryBanners(BuildContext context, List<CategoryModelCategorysCategorybanner> categoryBanners){
       List<Widget> list = new List();
        for(var i = 0; i < categoryBanners.length; i++){
          list.add(
              Container(
                width: (MediaQuery.of(context).size.width / 7 * 5  - 40.0)/3,
                child: Image.network(categoryBanners[i].imageUrl,fit:BoxFit.fill),
              )
              
          ); 
     }
     return list;
   }
 
   //标题栏
   Widget _buildTitle(dynamic head){
      return Padding(
        padding: const EdgeInsets.symmetric(horizontal: 10.0,vertical: 5.0),
        child: Row(
           mainAxisAlignment: MainAxisAlignment.spaceBetween,
           children: <Widget>[
              Text(head["Title"]),
              Text(head["UrlDesc"])
           ],
        ),
      );

   }
  //分类展示
   Widget _buildCategoryShow(BuildContext context,List<CategoryModelCategorysAreasContent> contents){
     return Padding(
            padding: const EdgeInsets.symmetric(horizontal: 10.0),
            child: Wrap(
               alignment: WrapAlignment.spaceBetween,
               children: _categoryShow(context, contents)
            ),
        
     );
     
   }
   //分类展示遍历
   List<Widget> _categoryShow(BuildContext context,List<CategoryModelCategorysAreasContent> contents){
       List<Widget> list = new List();
        for(var i = 0; i < contents.length; i++){
          list.add(
              Container(
                //  height:  MediaQuery.of(context).size.height / 7 + 20.0,
                 width:  (MediaQuery.of(context).size.width / 7 * 5 - 30.0)/3,
                 child: Column(
                     mainAxisAlignment: MainAxisAlignment.center,
                     children: <Widget>[
                        SizedBox(height:25.0),
                       (contents[i].text != null) ? CircleAvatar(
                           child: Image.network(contents[i].imageUrl),
                        ) : Padding(
                           padding: const EdgeInsets.symmetric(horizontal: 5.0),
                           child:Image.network(contents[i].imageUrl),
                        ),
                        SizedBox(height: 15.0,),
                        (contents[i].text != null) ? Text(
                          contents[i].text
                        ) : SizedBox(height: 1.0,),
                         
                     ],

                 ),
              )
              
          ); 
     }
     return list;
   }
}

这边布局也是比较简单的,主要就是考虑这个数据是否为null,来确定显示哪一部分。

结尾:这个页面的分类项点击的逻辑还没做,这个路由的配置就利用首页来做就好了,具体的分类列表项布局还算简单,就是其中的逻辑比较杂。其次这个搜索逻辑也没有做,看后面有时间补上。商品详情页

推荐阅读更多精彩内容