Flutter劫富济贫计划(五)——flutter布局实战

高仿商城详情布局

商品页为模仿京东商城布局,详情页为京东商城的WebView
在这里插入图片描述
这是我自己封装的屏幕适配插件,可忽略不计

import 'package:project/plugins/ScreenAdapter.dart';
主要代码
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:project/plugins/ScreenAdapter.dart';		
import 'package:flutter_inappwebview/flutter_inappwebview.dart';    // 嵌套HTML WebView

import 'package:project/pages/model/productContent_model.dart';     // 接口Model

import 'package:project/config/api.dart';     // 接口请求商品数据

/*
  商品详情
 */

class CommodityDetails extends StatefulWidget {
  final Map arguments;

  CommodityDetails({Key key, this.arguments}) : super(key: key);

  @override
  _CommodityDetailsState createState() => _CommodityDetailsState();
}


class _CommodityDetailsState extends State<CommodityDetails>{

  Api api = new Api();

  List<Map> arr = [
    {'cate':'颜色', 'list':[{'title':'碳纤黑', 'checked':true}, {'title':'冰川蓝', 'checked':false}, {'title':'火焰红', 'checked':false}, {'title':'夏之密语', 'checked':false}]},
    {'cate':'版本', 'list':[{'title':'6GB+64GB', 'checked':true}, {'title':'6GB+128GB', 'checked':false}, {'title':'8GB+128GB', 'checked':false}, {'title':'6GB+256GB', 'checked':false},]},
    {'cate':'购买方式', 'list':[{'title':'官方标配', 'checked':true}]},
  ];
  List<Map> currentList = [];

  String _selectValue;

  @override

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _getContentData();
    _initAttr();      // 修改源数据(第一种渲染方法)
    _getSelectedAttrValue();
  }

  _getContentData(){
      api.jd_getData('productList', formData: {'id':'59f6a2d27ac40b223cfdcf81'}).then((res){
        // print(json.decode(res.toString()));      // 转为Map类型
        var productContent = ProductContentModel.fromJson(res.data);      // 利用Model数据类型
        print(productContent.result.title);
      });
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 3,
      initialIndex: 0,
      child: Scaffold(
        appBar: AppBar(
          title: Center(
            child:Container(
              alignment: Alignment.center,
              width: ScreenAdapter.setWidth(400),
              child: TabBar(
                indicatorColor: Colors.white,
                // indicatorSize: TabBarIndicatorSize.label,         // 选中长度
                tabs: <Widget>[
                  Tab(
                    child: Text('商品'),
                  ),
                  Tab(
                    child: Text('详情'),
                  ),
                  Tab(
                    child: Text('评价'),
                  ),
                ],
              ),
            )
          ),
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.more_horiz),
              onPressed: (){
                  // 下拉菜单
                  showMenu(
                    context: context,
                    // 定位下拉菜单的位置
                    position:RelativeRect.fromLTRB(ScreenAdapter.setWidth(600), 80, 10, 0),
                    items: [
                      PopupMenuItem(
                        child: Row(
                          children: <Widget>[
                            Icon(Icons.home),
                            Text('首页')
                          ],
                        ),
                      ),
                      PopupMenuItem(
                        child: Row(
                          children: <Widget>[
                            Icon(Icons.search),
                            Text('搜索')
                          ],
                        ),
                      )
                    ]
                  );
              },
            )
          ],
        ),
        body:Stack(
          children: <Widget>[
            TabBarView(
              children: <Widget>[
                shoppingWidget(context),
                detailsWidget(),
                commentWidget()
              ],
            ),
            // 底部购物栏
            Positioned(
              width: ScreenAdapter.setWidth(750),
              height: ScreenAdapter.setHeight(100),
              child: Container(
                child: Row(
                  children: <Widget>[
                    Container(
                      width: 100,
                      height: ScreenAdapter.setHeight(100),
                      padding: EdgeInsets.only(top:ScreenAdapter.setHeight(10)),
                      child: Column(
                        children: <Widget>[
                          Icon(Icons.shopping_cart),
                          Text('购物车')
                        ],
                      ),
                    ),
                    Expanded(
                      flex: 1,
                      child: shoppingButtonWidget(color:Colors.orangeAccent, text: '加入购物车'),
                    ),
                    Expanded(
                      flex: 1,
                      child: shoppingButtonWidget(color:Colors.redAccent, text:'立即购买'),
                    ),
                  ],
                ), 
                decoration: BoxDecoration(
                  color: Colors.white,
                  border: Border(
                    top: BorderSide(width: 0.5, color: Colors.black12)
                  )
                ),
              ),
              bottom: 0,
            )
          ],
        )
      ),
    );
  }



// 初始化数据,将返回的List数据增加一个true和false
// 有两种办法:一种是让后台返回带有checked的数据,一种是创建一个空数组来接收转换后的数据(下面提供转换数据的方法)
_initAttr(){
  var newArr = arr;
  for(var i = 0;i<newArr.length;i++){
    for(var j = 0; j<newArr[i]['list'].length;j++){
      if(j==0){
        currentList.add({
          'title':newArr[i]['list'][j],
          'checked':true
        });
      }else{
        currentList.add({
          'title':newArr[i]['list'][j],
          'checked':false
        });
      }
    }
  }
  print(currentList);
}

// 点击修改checked
_changeChecked(cate, title, setBottomState){
  var newArr = arr;
  for(var i = 0; i<newArr.length;i++){
    // 如果循环出来的cate和点击的cate相等
    if(newArr[i]['cate'] == cate){
      for(var j = 0;j<newArr[i]['list'].length;j++){ 
        newArr[i]['list'][j]['checked'] = false;
        if(title == newArr[i]['list'][j]['title']){
          newArr[i]['list'][j]['checked'] = true;
        }
      }
    }
  }
  setBottomState(() {     // 注意 改变showModalBottomState的数据(来源于StatefulBuilder)
    this.arr = newArr;
  });
  _getSelectedAttrValue();
}

// 获取选中的值
_getSelectedAttrValue(){
  var newArr = arr;
  List tempArr = [];
  for(var i = 0; i<newArr.length;i++){
    for(var j = 0; j<newArr[i]['list'].length;j++){
      if(newArr[i]['list'][j]['checked'] == true){
        tempArr.add(newArr[i]['list'][j]['title']);
      }
    }
  }
  setState(() {
    this._selectValue = tempArr.join(',');
  });
}

// 底部弹出框 选择类型按钮
_getAttrItem(attrItem, setBottomState){
  List<Widget> attrItemList = [];

  attrItem['list'].forEach((val){
    attrItemList.add(
        Container(
          margin: EdgeInsets.all(ScreenAdapter.setWidth(10)),
          child: InkWell(
            onTap: (){
              _changeChecked(attrItem['cate'], val['title'], setBottomState);
            },
            child: Chip(
              padding: EdgeInsets.all(ScreenAdapter.setWidth(10)),
              backgroundColor: val['checked'] ? Colors.redAccent : null,
              label: Text('${val['title']}', style: TextStyle(color: val['checked'] ? Colors.white : null),),
            ),
          )
        ),
    );
  });
  return attrItemList;
}

// 购买选择 底部弹出框
_attrBottomSheet(context){
    showModalBottomSheet(
      context: context,
      builder: (context){
        return StatefulBuilder(
          builder: (BuildContext context, setBottomState){
            return Container(
                    height: ScreenAdapter.setHeight(600),
                    child: Stack(
                      children: <Widget>[
                        Container(
                          padding: EdgeInsets.all(10),
                          margin: EdgeInsets.only(bottom: ScreenAdapter.setHeight(80)),
                          child: ListView(
                            children: <Widget>[
                              // 外面为什么外面包一个Wrap?
                              // 如果直接使用Text和Wrap,会内容溢出,利用Wrap来自适应宽度
                              Column(
                                children:arr.map((val){
                                  return Wrap(
                                    children: <Widget>[
                                      Container(
                                        width: ScreenAdapter.setWidth(110),
                                        padding: EdgeInsets.only(top:ScreenAdapter.setHeight(25)),
                                        child: Text('${val['cate']}:', style: TextStyle(fontWeight: FontWeight.bold),),
                                      ),
                                      // wrapTitle(),
                                      Container(
                                        width: ScreenAdapter.setWidth(600),
                                        child: Wrap(
                                          children:_getAttrItem(val, setBottomState)
                                        ),
                                      )
                                    ],
                                  );
                                }).toList()
                              )
                            ],
                          ),
                        ),
                        Positioned(
                          bottom: 0,
                          width: ScreenAdapter.setWidth(750),
                          height: ScreenAdapter.setHeight(80),
                          child: Container(
                            color: Colors.white,
                            child: Row(
                              children: <Widget>[
                                Expanded(
                                  flex: 1,
                                  child: shoppingButtonWidget(color: Colors.orangeAccent, text: '加入购物车'),
                                ),
                                Expanded(
                                  flex: 1,
                                  child: shoppingButtonWidget(color: Colors.redAccent, text: '立即购买'),
                                )
                              ],
                            ),
                          )
                        )
                      ],
                    )
                  );
          },
        );
      }
    );
  }

// 商品页面
Widget shoppingWidget(context){
  return Container(
    margin: EdgeInsets.only(bottom: ScreenAdapter.setHeight(80)),
    child:ListView(
      children: <Widget>[
        AspectRatio(
          aspectRatio: 16/16,
          child: Image.network('http://m.360buyimg.com/mobilecms/s750x750_jfs/t1/53679/32/1028/217626/5cebc494E799aca0e/b9b78195adbb4bc4.jpg!q80.dpg', fit:BoxFit.cover),
        ),
        
        // 标题,副标题
        Container(
          color: Colors.white,
          padding:EdgeInsets.fromLTRB(ScreenAdapter.setWidth(10), 0, ScreenAdapter.setWidth(10) , ScreenAdapter.setWidth(20)),
          child: Column(
            children: <Widget>[
              Container(
                padding: EdgeInsets.only(top: ScreenAdapter.setHeight(20)),
                child: Row(
                  children: <Widget>[
                    Expanded(
                      flex: 1,
                      child: Row(
                        children: <Widget>[
                          Text('特价:'),
                          Text('¥2399.00', style: TextStyle(color: Colors.redAccent, fontSize: ScreenAdapter.size(45)),)
                        ],
                      ),
                    ),
                    Expanded(
                      flex: 1,
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.end,
                        children: <Widget>[
                          Text('原价:'),
                          Text('¥2899.00', style: TextStyle(
                            color: Colors.grey,
                            fontSize: ScreenAdapter.size(28),
                            decoration: TextDecoration.lineThrough
                          ),)
                        ],
                      ),
                    )
                  ],
                ),
              ),
              Container(
                padding: EdgeInsets.only(top:ScreenAdapter.setHeight(15)),
                child: Text(
                  'Redmi K20Pro 骁龙855 索尼4800万超广角三摄 AMOLED弹出式全面屏 8GB+256GB 冰川蓝 游戏智能手机 小米 红米',
                  style: TextStyle(
                    color: Colors.black87,
                    fontSize: ScreenAdapter.size(35)
                  ),
                ),
              ),
              Container(
                padding: EdgeInsets.only(top:ScreenAdapter.setHeight(10)),
                child: Text(
                  '【骁龙855旗舰处理器,索尼4800万超广角三摄】巨惠来袭,数量有限,售完即止。',
                  style: TextStyle(
                    color: Colors.black54,
                    fontSize: ScreenAdapter.size(28)
                  ),
                ),
              ),
            ],
          ),
        ),
        
        // 优惠
        Container(
          color: Colors.white,
          margin: EdgeInsets.fromLTRB(0, ScreenAdapter.setHeight(10), 0, ScreenAdapter.setHeight(10)),
          padding: EdgeInsets.all(ScreenAdapter.setHeight(10)),
          child: Column(
            children: <Widget>[
              Container(
                child: Row(
                  // mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    Container(
                      alignment: Alignment.topCenter,
                      height: ScreenAdapter.setHeight(60),
                      child: Text('优惠:', style: TextStyle(fontSize: ScreenAdapter.size(25), color: Colors.grey)),
                    ),
                    Container(
                      padding: EdgeInsets.only(left: ScreenAdapter.setWidth(20)),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          RichText(
                            text: TextSpan(
                              style: TextStyle(color: Colors.black, fontSize: ScreenAdapter.size(25)),
                              children: <TextSpan>[
                                TextSpan(
                                  text: '免费领 ',
                                  style: TextStyle(color: Colors.redAccent)
                                ),
                                TextSpan(
                                  text: ' 体验卡免费领,超大流量任意使用'
                                )
                              ]
                            ),
                          ),
                          RichText(
                            text: TextSpan(
                              style: TextStyle(color: Colors.black, fontSize: ScreenAdapter.size(25)),
                              children: <TextSpan>[
                                TextSpan(
                                  text: '优惠套装 ',
                                  style: TextStyle(color: Colors.redAccent)
                                ),
                                TextSpan(
                                  text: ' 该商品共有10款优惠套装'
                                )
                              ]
                            ),
                          ),
                        ],
                      ),
                    ),
                    Expanded(
                      flex: 1,
                      child: Container(
                        alignment: Alignment.centerRight,
                        child: Icon(Icons.more_horiz),
                      ),
                    )
                  ],
                ),
              ),
            ],
          ),
        ),
        
        // 选择商品,收货地址
        Container(
          color: Colors.white,
          margin: EdgeInsets.fromLTRB(0, ScreenAdapter.setHeight(10), 0, ScreenAdapter.setHeight(10)),
          padding: EdgeInsets.all(ScreenAdapter.setHeight(10)),
          child: Column(
            children: <Widget>[
              Container(
                child: InkWell(
                  onTap: (){
                    _attrBottomSheet(context);
                  },
                  child: Row(
                    // mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: <Widget>[
                      Container(
                        alignment: Alignment.topCenter,
                        height: ScreenAdapter.setHeight(60),
                        child: Text('已选:', style: TextStyle(fontSize: ScreenAdapter.size(25), color: Colors.grey)),
                      ),
                      Container(
                        padding: EdgeInsets.only(left: ScreenAdapter.setWidth(20)),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: <Widget>[
                            RichText(
                              text: TextSpan(
                                style: TextStyle(color: Colors.black, fontSize: ScreenAdapter.size(25)),
                                children: <TextSpan>[
                                  TextSpan(
                                    text: '${_selectValue}'
                                  )
                                ]
                              ),
                            ),
                            RichText(
                              text: TextSpan(
                                style: TextStyle(color: Colors.grey, fontSize: ScreenAdapter.size(25)),
                                children: <TextSpan>[
                                  TextSpan(
                                    text: '本商品支持保障服务,京东服务, 点击可选服务'
                                  )
                                ]
                              ),
                            ),
                          ],
                        ),
                      ),
                      Expanded(
                        flex: 1,
                        child: Container(
                          alignment: Alignment.centerRight,
                          child: Icon(Icons.more_horiz),
                        ),
                      )
                    ],
                  ),
                )
              ),
              Divider(),
              // 收货地址
              Container(
                child: Row(
                  // mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    Container(
                      alignment: Alignment.topCenter,
                      height: ScreenAdapter.setHeight(60),
                      child: Text('送至:', style: TextStyle(fontSize: ScreenAdapter.size(25), color: Colors.grey)),
                    ),
                    Container(
                      padding: EdgeInsets.only(left: ScreenAdapter.setWidth(20)),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          RichText(
                            text: TextSpan(
                              style: TextStyle(color: Colors.black, fontSize: ScreenAdapter.size(25)),
                              children: <TextSpan>[
                                TextSpan(
                                  text: '北京朝阳区三环到四环之间 ',
                                ),
                              ]
                            ),
                          ),
                          RichText(
                            text: TextSpan(
                              style: TextStyle(color: Colors.grey, fontSize: ScreenAdapter.size(25)),
                              children: <TextSpan>[
                                TextSpan(
                                  text: '现货',
                                  style: TextStyle(color: Colors.red)
                                ),
                                TextSpan(
                                  text: '23:00前下单,预计明天(12月02日)送达'

                                )
                              ]
                            ),
                          ),
                        ],
                      ),
                    ),
                    Expanded(
                      flex: 1,
                      child: Container(
                        alignment: Alignment.centerRight,
                        child: Icon(Icons.more_horiz),
                      ),
                    )
                  ],
                ),
              ),
            ],
          ),
        ),
      ],
    )
  );
}

// 详情页面
// 为避免每次进入详情都会再次加载一遍页面的情况出现,可以继承一下automatikeepalive实现状态保持
// 所以要将该详情页面抽离成一个stful状态控件
Widget detailsWidget(){
  return Container(
    child: Column(
      children: <Widget>[
        Text('我是顶部数据'),
        Expanded(
              child: Container(
                child: InAppWebView(
                  initialUrl: "https://item.m.jd.com/ware/view.action?wareId=100003582705&sid=null",
                  initialHeaders: {},
                  initialOptions: InAppWebViewWidgetOptions(
                      inAppWebViewOptions: InAppWebViewOptions(
                        debuggingEnabled: true,
                      )
                  ),
                  onWebViewCreated: (InAppWebViewController controller) {
  
                  },
                  onLoadStart: (InAppWebViewController controller, String url) {
  
                  },
                  onLoadStop: (InAppWebViewController controller, String url) {
  
                  },
                ),
              ),
        ),
        Text('我是底部数据'),
      ],
    )
  );
}

// 评价页面
Widget commentWidget(){
  return Container(
    child: Text('评价11111111'),
  );
}

// 底部购买按钮
Widget shoppingButtonWidget({Color color = Colors.black, String text = '按钮', Object fn = null}){
  return InkWell(
    onTap: fn,
    child: Container(
      height: ScreenAdapter.setHeight(70),
      width: double.infinity,
      margin: EdgeInsets.all(5),
      alignment: Alignment.center,
      child: Text(text, style: TextStyle(color: Colors.white),),
      decoration: BoxDecoration(
        color: color,
        borderRadius: BorderRadius.circular(10)
      ),
    ),
  );
}



}
productContent_model.dart页面Model数据代码
class ProductContentModel {
  ProductContentItem result;

  ProductContentModel({this.result});

  ProductContentModel.fromJson(Map<String, dynamic> json) {
    result =
        json['result'] != null ? new ProductContentItem.fromJson(json['result']) : null;
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    if (this.result != null) {
      data['result'] = this.result.toJson();
    }
    return data;
  }
}

class ProductContentItem {
  String sId;
  String title;
  String cid;
  int price;
  String oldPrice;
  int isBest;
  int isHot;
  int isNew;
  String status;
  String pic;
  String content;
  String cname;
  List<Attr> attr;
  String subTitle;
  int salecount;

  ProductContentItem(
      {this.sId,
      this.title,
      this.cid,
      this.price,
      this.oldPrice,
      this.isBest,
      this.isHot,
      this.isNew,
      this.status,
      this.pic,
      this.content,
      this.cname,
      this.attr,
      this.subTitle,
      this.salecount});

  ProductContentItem.fromJson(Map<String, dynamic> json) {
    sId = json['_id'];
    title = json['title'];
    cid = json['cid'];
    price = json['price'];
    oldPrice = json['old_price'];
    isBest = json['is_best'];
    isHot = json['is_hot'];
    isNew = json['is_new'];
    status = json['status'];
    pic = json['pic'];
    content = json['content'];
    cname = json['cname'];
    if (json['attr'] != null) {
      attr = new List<Attr>();
      json['attr'].forEach((v) {
        attr.add(new Attr.fromJson(v));
      });
    }
    subTitle = json['sub_title'];
    salecount = json['salecount'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['_id'] = this.sId;
    data['title'] = this.title;
    data['cid'] = this.cid;
    data['price'] = this.price;
    data['old_price'] = this.oldPrice;
    data['is_best'] = this.isBest;
    data['is_hot'] = this.isHot;
    data['is_new'] = this.isNew;
    data['status'] = this.status;
    data['pic'] = this.pic;
    data['content'] = this.content;
    data['cname'] = this.cname;
    if (this.attr != null) {
      data['attr'] = this.attr.map((v) => v.toJson()).toList();
    }
    data['sub_title'] = this.subTitle;
    data['salecount'] = this.salecount;
    return data;
  }
}

class Attr {
  String cate;
  List<String> list;

  Attr({this.cate, this.list});

  Attr.fromJson(Map<String, dynamic> json) {
    cate = json['cate'];
    list = json['list'].cast<String>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['cate'] = this.cate;
    data['list'] = this.list;
    return data;
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值