美文网首页flutterflutter
Flutter入门实战之(一)商城首页界面(轮播图、分类导航、商

Flutter入门实战之(一)商城首页界面(轮播图、分类导航、商

作者: vue爱好者 | 来源:发表于2019-11-07 17:30 被阅读0次

    效果图:

    首页

    代码分析:

    首先看一下整个首页界面需要导入的包文件。

    import 'dart:core';  // dart包
    import 'package:flutter/material.dart'; // flutter包,只有导入了它才可以使用各种flutter的各种Widget组件
    import 'package:dio/dio.dart'; // 一个用于发送http请求的包(第三方插件)
    import 'package:native_app/view/classify/index.dart'; // 分类页面(底部导航用到)
    import 'package:native_app/view/find/list.dart'; // 发现页面(底部导航用到)
    import 'package:native_app/view/user/cart.dart'; // 购物车页面(底部导航用到)
    import 'package:native_app/view/user/index.dart'; // 个人中心页面(底部导航用到)
    
    import 'package:flutter_swiper/flutter_swiper.dart'; // 轮播图(第三方插件)
    import 'package:native_app/model/product.dart'; // 用于解析请求回来的商品数据JSON
    import 'package:native_app/model/classify.dart'; // 用于解析请求回来的分类数据JSON
    import '../../components/NavList.dart'; // 分类导航区域布局
    import '../../components/ProductList.dart'; // 首页展示商品列表布局组件
    import '../../config/web.config.dart'; // 接口配置文件
    import '../../components/NavBottomItems.dart'; // 底部导航组件
    
    import '../../router/application.dart'; // 路由配置
    import '../../utils/utils.dart'; // 一个常用工具库
    

    导入的包就不多说了,大家看注释也能看到是做什么用的。继续往下看。

    可以看到定义了两个请求函数,作用也很明显,获取 “商品数据” 和 “获取分类列表”

    // 获取商品列表
    Future getProductList() async {
      try {
        var dio = Dio();
        var url = webApi['productList'];
        Response response = await dio.get("$url?page=1&limit=10");
        return response.data;
      } catch (e) {
        print(e);
      }
    }
    
    // 获取分类列表
    Future getCategoryList() async {
      try {
        var url = webApi['categoryList'];
        Response response = await Dio().get(url);
        return response.data;
      } catch (e) {
        print(e);
      }
    }
    

    对这两个函数做做一下简单的说明,(其实不用解释大家都懂是吧....)
    var dio = Dio(); // 实例化一个Dio对象,这个对象用来发送请求的
    webApi['productList'] // 从我们上面的接口配置文件(web.config.dart)获取一个接口URL
    Response response = await Dio().get(url); // 通过get方式获取数据

    class HomePage extends StatefulWidget {
      HomePage({Key key}) : super(key: key);
    
      @override
      _HomePageState createState() => _HomePageState();
    }
    

    对于上面的代码引用一下flutter中文网的一句话:

    Stateful widgets 持有的状态可能在 widget 生命周期中发生变化,实现一个 stateful widget 至少需要两个类:
    1、一个 StatefulWidget 类;
    2、一个 State 类,StatefulWidget 类本身是不变的,但是 State 类在 widget 生命周期中始终存在。

    class _HomePageState extends State<HomePage> {
      List<ProductData> productList = <ProductData>[]; // 商品列表
      List<NavList> navServeList = []; // 分类导航
    

    定义两个数组用来保存 “商品列表数据” 和 “分类导航数据”

     // 分类导航列表
      Widget bodyGrid(List<NavList> menu) => SliverGrid(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 5,
              mainAxisSpacing: 0.0,
              crossAxisSpacing: 0.0,
              childAspectRatio: 0.9,
            ),
            delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
              var params = menu[index].name + menu[index].id.toString();
              return InkWell(
                onTap: () {
                  Application.router.navigateTo(
                      context, "/productList/${Uri.encodeComponent(params)}");
                },
                child: NavList(
                  name: menu[index].name,
                  img: 'http://47.107.101.76/' + menu[index].img,
                ),
              );
            }, childCount: navServeList.length),
          );
    

    首页轮播图下面的分类导航布局,
    采用SliverGrid宫格Widget,每行显示5个,点击对应的分类跳转到分类商品列表页面,
    值得注意的是,因为路由传过去的参数带有中文,所有需要Uri.encodeComponent()编码一下,不然会报错误。

    // 商品列表
      Widget bodyProductList(List<ProductData> shop) => SliverGrid(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 2,
              mainAxisSpacing: 1.0,
              crossAxisSpacing: 1.0,
              childAspectRatio: 0.7,
            ),
            delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
              return ProductList(
                id: shop[index].id,
                url: shop[index].thumbnail,
                price: shop[index].price,
                market: shop[index].orignPrice,
                name: shop[index].title,
              );
            }, childCount: productList.length),
          );
    

    商品列表和分类导航一样都是采用SliverGrid宫格Widget

    @override
      initState() {
        // 获取商品列表数据
        getProductList().then((data) {
          Product product = Product.fromJson(data);
          List<ProductData> showData = <ProductData>[];
          product.data.forEach((v) => {showData.add(v)});
          setState(() {
            productList = showData;
          });
        });
        // 获取分类列表数据
        getCategoryList().then((data) {
          Classify list = Classify.fromJson(data);
          List<NavList> showData = [];
          if (list.data.length <= 0) {
            return;
          }
          for (var i = 0; i < list.data.length; i++) {
            switch (i.toString()) {
              case '0':
                {
                  showData.addAll(navTakeMap(3, list.data[i].children));
                }
                break;
              case '1':
                {
                  showData.addAll(navTakeMap(2, list.data[i].children));
                }
                break;
              case '2':
                {
                  showData.addAll(navTakeMap(2, list.data[i].children));
                }
                break;
              case '3':
                {
                  showData.addAll(navTakeMap(2, list.data[i].children));
                }
                break;
              case '4':
                {
                  showData.addAll(navTakeMap(1, list.data[i].children));
                }
                break;
            }
          }
          setState(() {
            navServeList = showData;
          });
        });
        super.initState();
      }
    

    initState函数:
    主要是进行数据获取以及解析JSON数据。

     Product product = Product.fromJson(data); // 解析商品接口返回是json数据
    

    用到之前引入的(import 'package:native_app/model/product.dart'; )

    Classify list = Classify.fromJson(data); // 解析分类接口返回是json数据
    

    用到之前引入的(import 'package:native_app/model/classify.dart'; )

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

    销毁资源,不解释

     // 轮播图数据
      var recommend = [
        "http://47.107.101.76/static/upload/1566643032.webp",
        "http://47.107.101.76/static/upload/1566644192.webp",
        "http://47.107.101.76/static/upload/1566643284.webp",
        "http://47.107.101.76/static/upload/1566642477.webp",
        "http://47.107.101.76/static/upload/1566645256.webp",
        "http://47.107.101.76/static/upload/1566642775.webp",
        "http://47.107.101.76/static/upload/1566642251.webp",
        "http://47.107.101.76/static/upload/1566643913.webp"
      ];
    
      int _selectedIndex = 0; // 底部导航索引
     // 底部导航点击事件
      void _onItemTapped(int index) {
        if(index == 0) {
          return;
        }
        if (index == 3) {
          Application.router.navigateTo(context, "/cart");
          return;
        }
        this.setState((){
          _selectedIndex = index;
        });
      }
    

    目前轮播图数据都是写死的,后期会改成从接口获取,接口还没来得及写呢。。。
    _onItemTapped是一个点击事件触发的函数,接收一个int 型的参数,
    如果是0代表就是首页,直接return,如果是3的话,进行路由跳转至购物车,其他的就更新_selectedIndex的值就行。

    // 首页布局
      Widget HomePageUI() {
        return Scaffold(
          appBar: AppBar(
            centerTitle: true,
            title: Text(
              "强野绿色生活",
              style: TextStyle(color: Colors.white),
            ),
          ),
          body: CustomScrollView(
            shrinkWrap: true,
            slivers: <Widget>[
              new SliverPadding(
                padding: const EdgeInsets.all(0.0),
                sliver: new SliverList(
                    delegate: new SliverChildListDelegate(<Widget>[
                  Container(
                    color: Colors.white,
                    width: MediaQuery.of(context).size.width,
                    height: MediaQuery.of(context).size.width - 100.0,
                    margin: EdgeInsets.only(bottom: 10.0),
                    child: new Swiper(
                      itemBuilder: (BuildContext context, int index) {
                        return new Image.network(
                          recommend[index],
                          fit: BoxFit.contain,
                        );
                      },
                      itemCount: recommend.length,
                      pagination: new SwiperPagination(
                          builder: DotSwiperPaginationBuilder(
                              size: 6.0, activeSize: 6.0, color: Colors.grey)),
                      autoplay: true,
                    ),
                  ),
                ])),
              ),
              new SliverPadding(
                padding: const EdgeInsets.all(0.0),
                sliver: new SliverList(
                    delegate: new SliverChildListDelegate(<Widget>[
                  Container(
                    height: 10.0,
                    color: Colors.white,
                  )
                ])),
              ),
              bodyGrid(navServeList),
              new SliverPadding(
                padding: const EdgeInsets.all(0.0),
                sliver: new SliverList(
                    delegate: new SliverChildListDelegate(<Widget>[
                  Container(
                    width: MediaQuery.of(context).size.width,
                    margin: EdgeInsets.only(bottom: 10.0),
                  ),
                ])),
              ),
              bodyProductList(productList),
            ],
          ),
          bottomNavigationBar: BottomNavigationBar(
            items: navBottomItems,
            currentIndex: _selectedIndex,
            onTap: _onItemTapped,
            type: BottomNavigationBarType.fixed,
          ),
        );
      }
    

    上面代码就是首页的整个布局,很简单,不多说了。

    @override
      Widget build(BuildContext context) {
        switch (this._selectedIndex.toString()) {
          case '0':
            {
             return HomePageUI();
            }
            break;
          case '1':
            {
             return ClassifyIndex();
            }
            break;
          case '2':
            {
             return FindPage();
            }
            break;
          case '3':
            {
             return CartPage();
            }
            break;
          case '4':
            {
             return UserPage();
            }
            break;
          default:
          {
           return HomePageUI();
          }
        }
      }
    

    最后就是根据 "_selectedIndex"的值,渲染UI。完成

    完整代码:

    https://github.com/AntJavascript/flutter-shop/blob/master/lib/view/home/index.dart

    相关文章

      网友评论

        本文标题:Flutter入门实战之(一)商城首页界面(轮播图、分类导航、商

        本文链接:https://www.haomeiwen.com/subject/agoovctx.html