19.首页_火爆专区界面布局编写

看一下图片的效果

一个标题栏,下面是多行两列。里面可以用column布局,外面用Warp流式布局

有得小伙伴说这里可以用网格布局,网格布局的话还是有一定的效率问题。这里就用我们的流布局,还是很顺畅的

填一下上节课的坑,设置可选参数

service_method.dart 这里用花括号括起来就是个可选的参数了。

修改成可选参数后呢,我们在调用方法的时候就报错了。

我们只要修改为key/value的形式就可以了

火爆专区的请求数据

上拉刷新有几种方式

火爆专区主要的代码,这是之前的,现在要删掉了

//火爆专区 定义为动态的类
class HotGoods extends StatefulWidget {
@override
_HotGoodsState createState() => _HotGoodsState();
} class _HotGoodsState extends State<HotGoods> {
void initState() {
super.initState();
request('homePageBelowConten',formData:).then((val){
print(val);
});
}
@override
Widget build(BuildContext context) {
return Container(
child:Text('')
);
}
}

我们在最上面定义两个变量。使用的是State For Widget的形式

定义一个内部的方法去获取远程数据,内部方法用下划线开头 _getHotGoods()

创建火爆专区的标题

//火爆专区的标题
Widget hotTitle = Container(
margin: EdgeInsets.only(top:10.0),//上边距
alignment: Alignment.center,//居中对齐
color: Colors.transparent,
child: Text('火爆专区'),
);

流式布局

流式布局的问题,如果数据是空 那么返回流式布局就会报错

所以我们需要判断数据的list的长度

把list包装成Widget

把商品的List<Map>的形式,转换成了 List<Widget>的形式

最终再返回我们的wrap布局

else的情况,当列表数据为空那么我们就返回一个空的Text widget

把标题和流式布局组合起来

调用加载数据的方法

测试效果展示

往下拖还有一些数据

标题加上内边距

边距稍微好看了点

最终代码

import 'package:flutter/material.dart';
import '../service/service_method.dart';
import 'package:flutter_swiper/flutter_swiper.dart';
import 'dart:convert';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:url_launcher/url_launcher.dart'; class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
} class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin{ int page=;
List<Map> hotGoodsList=[];
@override
bool get wantKeepAlive => true; @override
void initState() {
super.initState();
_getHotGoods();
print('');
} String homePageContent='正在获取数据';
@override
Widget build(BuildContext context) {
var formData={'lon':'115.02932','lat':'35.76189'};//传一个经纬度过去,防止恶意下单
return Scaffold(
appBar: AppBar(title: Text('百姓生活+')),
body: FutureBuilder(
future: request('homePageContent',formData:formData),
builder: (context, snapshot) {
if(snapshot.hasData){
var data=json.decode(snapshot.data.toString());
List<Map> swiper=(data['data']['slides'] as List).cast();
List<Map> navigatorList=(data['data']['category'] as List).cast();
String adPicture=data['data']['advertesPicture']['PICTURE_ADDRESS'];
String leaderImage=data['data']['shopInfo']['leaderImage'];
String leaderPhone=data['data']['shopInfo']['leaderPhone'];
List<Map> recommendList=(data['data']['recommend'] as List).cast();
String floor1Title=data['data']['floor1Pic']['PICTURE_ADDRESS'];
String floor2Title=data['data']['floor2Pic']['PICTURE_ADDRESS'];
String floor3Title=data['data']['floor3Pic']['PICTURE_ADDRESS'];
List<Map> floor1=(data['data']['floor1'] as List).cast();
List<Map> floor2=(data['data']['floor2'] as List).cast();
List<Map> floor3=(data['data']['floor3'] as List).cast(); return SingleChildScrollView(
child: Column(
children: <Widget>[
SwiperDiy(swiperDateList: swiper),
TopNavigator(navigatorList:navigatorList ,),
AdBanner(adPicture:adPicture),
LeaderPhone(leaderImage: leaderImage,leaderPhone: leaderPhone,),
Recommend(recommendList:recommendList),
FloorTitle(picture_address: floor1Title,),//楼层1的标题图片
FloorContent(floorGoodsList: floor1),
FloorTitle(picture_address: floor2Title,),//楼层1的标题图片
FloorContent(floorGoodsList: floor2),
FloorTitle(picture_address: floor3Title,),//楼层1的标题图片
FloorContent(floorGoodsList: floor3),
_hotGoods()
],
),
);
}else{
return Center(child: Text('加载中....'));
}
},
),
);
} void _getHotGoods(){
var formData={'page',page};
request('homePageBelowConten',formData:formData).then((val){
var data=json.decode(val.toString());
List<Map> newGoodsList=(data['data'] as List).cast();
//把新的列表加到老的列表里面
setState(() {
hotGoodsList.addAll(newGoodsList);
page++;
});
});
}
//火爆专区的标题
Widget hotTitle = Container(
margin: EdgeInsets.only(top:10.0),//上边距
alignment: Alignment.center,//居中对齐
color: Colors.transparent,
padding: EdgeInsets.all(5.0),
child: Text('火爆专区'),
); Widget _wrapList(){
if(hotGoodsList.length!=){
List<Widget> listWidget=hotGoodsList.map((val){
return InkWell(
onTap: (){},
child: Container(
width: ScreenUtil().setWidth(),
color: Colors.white,
padding: EdgeInsets.all(5.0),//内边距
margin: EdgeInsets.only(bottom: 3.0),
child: Column(
children: <Widget>[
Image.network(val['image'],width: ScreenUtil().setWidth(),),//设置宽度防止超出边界
Text(
val['name'],
maxLines: ,//只有一行
overflow: TextOverflow.ellipsis,//超出显示省略号的形式
style: TextStyle(color: Colors.pink,fontSize: ScreenUtil().setSp()),
),
Row(
children: <Widget>[
Text('¥${val['mallPrice']}'),//商城价格
Text(
'¥${val['price']}',
style: TextStyle(color: Colors.black26,decoration: TextDecoration.lineThrough),//加上删除线的价格
)
],
)
],
),
),
);
}).toList();
return Wrap(
spacing: ,//每一行显示两列
children: listWidget,
);
}else{
return Text('');
}
} //组合火爆专区的标题和列表
Widget _hotGoods(){
return Container(
child: Column(
children: <Widget>[
hotTitle,
_wrapList()
],
),
);
} }
//首页轮播插件
class SwiperDiy extends StatelessWidget {
final List swiperDateList;
//构造函数
SwiperDiy({this.swiperDateList}); @override
Widget build(BuildContext context) { // print('设备的像素密度:${ScreenUtil.pixelRatio}');
// print('设备的高:${ScreenUtil.screenWidth}');
// print('设备的宽:${ScreenUtil.screenHeight}'); return Container(
height: ScreenUtil().setHeight(),//
width:ScreenUtil().setWidth(),
child: Swiper(
itemBuilder: (BuildContext context,int index){
return Image.network("${swiperDateList[index]['image']}",fit: BoxFit.fill,);
},
itemCount: swiperDateList.length,
pagination: SwiperPagination(),
autoplay: true,
),
);
}
} class TopNavigator extends StatelessWidget {
final List navigatorList; TopNavigator({Key key, this.navigatorList}) : super(key: key); Widget _gridViewItemUI(BuildContext context,item){
return InkWell(
onTap: (){print('点击了导航');},
child: Column(
children: <Widget>[
Image.network(item['image'],width: ScreenUtil().setWidth()),
Text(item['mallCategoryName'])
],
),
);
}
@override
Widget build(BuildContext context) {
if(this.navigatorList.length>){
this.navigatorList.removeRange(,this.navigatorList.length);//从第十个截取,后面都截取掉
}
return Container(
height: ScreenUtil().setHeight(),//只是自己大概预估的一个高度,后续可以再调整
padding: EdgeInsets.all(3.0),//为了不让它切着屏幕的边缘,我们给它一个padding
child: GridView.count(
crossAxisCount: ,//每行显示5个元素
padding: EdgeInsets.all(5.0),//每一项都设置一个padding,这样他就不挨着了。
children: navigatorList.map((item){
return _gridViewItemUI(context,item);
}).toList(),
),
);
}
} class AdBanner extends StatelessWidget {
final String adPicture; AdBanner({Key key, this.adPicture}) : super(key: key); @override
Widget build(BuildContext context) {
return Container(
child: Image.network(adPicture),
);
}
} //店长电话模块
class LeaderPhone extends StatelessWidget {
final String leaderImage;//店长图片
final String leaderPhone;//店长电话 LeaderPhone({Key key, this.leaderImage,this.leaderPhone}) : super(key: key); @override
Widget build(BuildContext context) {
return Container(
child: InkWell(
onTap: _launchURL,
child: Image.network(leaderImage),
),
);
} void _launchURL() async {
String url = 'tel:'+leaderPhone;
//String url = 'http://jspang.com';
if(await canLaunch(url)){
await launch(url);
}else{
throw 'url不能进行访问,异常';
}
}
} //商品推荐
class Recommend extends StatelessWidget {
final List recommendList; Recommend({Key key, this.recommendList}) : super(key: key); //商品标题
Widget _titleWidget(){
return Container(
alignment: Alignment.centerLeft,//局长靠左对齐
padding: EdgeInsets.fromLTRB(10.0, 2.0, , 5.0),//左上右下
decoration: BoxDecoration(
color: Colors.white,
border: Border(
bottom: BorderSide(width: 0.5,color: Colors.black12) //设置底部的bottom的边框,Black12是浅灰色
),
),
child: Text(
'商品推荐',
style:TextStyle(color: Colors.pink)
),
);
}
//商品单独项方法
Widget _item(index){
return InkWell(
onTap: (){},//点击事件先留空
child: Container(
height: ScreenUtil().setHeight(),//兼容性的高度 用了ScreenUtil
width: ScreenUtil().setWidth(),//750除以3所以是250
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.white,
border: Border(
left: BorderSide(width: ,color: Colors.black12)//右侧的 边线的样式 宽度和 颜色
)
),
child: Column(
children: <Widget>[
Image.network(recommendList[index]['image']),
Text('¥${recommendList[index]['mallPrice']}'),
Text(
'¥${recommendList[index]['price']}',
style: TextStyle(
decoration: TextDecoration.lineThrough,//删除线的样式
color: Colors.grey//浅灰色
),
),
],
),
),
);
}
//横向列表方法
Widget _recommendList(){
return Container(
height: ScreenUtil().setHeight(),
child: ListView.builder(
scrollDirection: Axis.horizontal,//横向的
itemCount: recommendList.length,
itemBuilder: (context,index){
return _item(index);
},
),
);
} @override
Widget build(BuildContext context) {
return Container(
height: ScreenUtil().setHeight(),//列表已经设置为330了因为还有上面标题,所以要比330高,这里先设置为380
margin: EdgeInsets.only(top: 10.0),
child: Column(
children: <Widget>[
_titleWidget(),
_recommendList()
],
),
);
}
} //楼层标题
class FloorTitle extends StatelessWidget {
final String picture_address; FloorTitle({Key key, this.picture_address}) : super(key: key); @override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8.0),
child: Image.network(picture_address),
);
}
} //楼层商品列表
class FloorContent extends StatelessWidget {
final List floorGoodsList; FloorContent({Key key, this.floorGoodsList}) : super(key: key); @override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
_firstRow(),
_otherGoods()
],
),
);
} Widget _firstRow(){
return Row(
children: <Widget>[
_goodsItem(floorGoodsList[]),
Column(
children: <Widget>[
_goodsItem(floorGoodsList[]),
_goodsItem(floorGoodsList[])
],
)
],
);
}
Widget _otherGoods(){
return Row(
children: <Widget>[
_goodsItem(floorGoodsList[]),
_goodsItem(floorGoodsList[])
],
);
}
Widget _goodsItem(Map goods){
return Container(
width: ScreenUtil().setWidth(),
child: InkWell(
onTap: (){print('点击了楼层商品');},
child: Image.network(goods['image']),
),
);
}
}

home_page.dart

Flutter实战视频-移动电商-19.首页_火爆专区界面布局编写的更多相关文章

  1. Flutter实战视频-移动电商-18.首页_火爆专区后台接口调试

    18.首页_火爆专区后台接口调试 楼层结束之后有个火爆专区.到地图有个上拉加载的效果 lib/config/service_url.dart 首先找到我们的接口配置文件,增加接口的配置 lib/ser ...

  2. Flutter实战视频-移动电商-20.首页_火爆专区上拉加载效果

    20.首页_火爆专区上拉加载效果 上拉加载的插件比较都.没有一个一枝独秀的 可以自定义酷炫的header和footer 一直在更新 推荐使用这个插件: https://github.com/xuelo ...

  3. Flutter实战视频-移动电商-09.首页_项目结构建立和获取数据

    09.首页_项目结构建立和获取数据 在config下创建service_url.dart 用来配置我们后端接口的配置文件 一个变量存 接口地址,一个接口方法地址 所有后天请求数据的方法都放在这个文件夹 ...

  4. Flutter实战视频-移动电商-11.首页_屏幕适配方案讲解

    11.首页_屏幕适配方案讲解 国人写的屏幕适配插件: https://github.com/OpenFlutter/flutter_screenutil 最新版本是0.5.1 在pubspec.yam ...

  5. Flutter实战视频-移动电商-13.首页_广告Banner组件制作

    13.首页_广告Banner组件制作 主要是做这个小广告条. 其实就是读取一个图片做一个widget放到这里 使用stlessW快速生成 定义一个变量存放图片的url地址: 这样我们的广告条就写完了 ...

  6. Flutter实战视频-移动电商-15.首页_商品推荐模块编写

    15.首页_商品推荐模块编写 商品推荐,我们做成可以横向滚动的 分析: 上面是标题,下面是ListView,里面是一个Column, column分三层,第一是图片,第二是价格,第三是市场价格 小细节 ...

  7. Flutter实战视频-移动电商-17.首页_楼层组件的编写技巧

    17.首页_楼层组件的编写技巧 博客地址: https://jspang.com/post/FlutterShop.html#toc-b50 楼层的效果: 标题 stlessW快速生成: 接收一个St ...

  8. Flutter实战视频-移动电商-54.购物车_商品列表子项布局

    54.购物车_商品列表子项布局 子项做成一个单独的页面 新建cartItem.dart文件 新建cart_page文件夹,在里面新建cart_item.dart页面, 页面名字叫做CartItem 定 ...

  9. Flutter实战视频-移动电商-10.首页_FlutterSwiper轮播效果制作

    10.首页_FlutterSwiper轮播效果制作 博客地址: https://jspang.com/post/FlutterShop.html#toc-5c2 flutter_swiper http ...

随机推荐

  1. MySQL windows集群(转)

    http://blog.csdn.net/zhangking/article/details/5670070    MySQL 群集是 MySQL 适合于分布式计算环境的高可用.高冗余版本.它采用了 ...

  2. ThinkPHP5 安装自定义模块

    安装官方给的demo,在build.php文件中 <?php // +-------------------------------------------------------------- ...

  3. 利用crtmpserver搭建rtmp服务器

    Google + 实践:最终直播成功. 记录一下. 这样.兴许就能够对代码进行改造,利用开源码实现:Android平台下.搭建rtmpserver.浏览器端利用flash播放视频. 代码架构为:ffm ...

  4. 2015年度新增开源软件排名TOP 100,EasyDarwin开源流媒体服务器排名第17

    本榜单包含 2015 年开源中国新收录的 5977 款开源软件中,根据软件本身的关注度.活跃程度进行排名前 100 名的软件.从这份榜单中或许可以了解到最新业界的趋势. 榜单详情:http://www ...

  5. 2017NOIP游记 (格式有点炸)

    NOIP游记 作者:一只小蒟蒻 时间可真快呀!还记得我第一次接触信息竞赛时,hello world都要调好久,不知不觉就考完了2017noip,自我感觉良好(虽然还是有很多不足). 这两个月的闭关,让 ...

  6. FIL代币是什么?

    自从比特币价格暴涨.区块链技术火了以后,出现了币圈,币圈中有各种各样的代币,本文就和大家介绍其中的FIL代币相关内容,希望能帮助大家一点一点的了解币圈.        IPFS与Filecoin的关系 ...

  7. 消息handler message 线程通信 空消息

    空消息的使用 private Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { ...

  8. python不同目录下的调用

    转自http://blog.csdn.net/hansel/article/details/8975663 Python包含子目录中的模块方法比较简单,关键是能够在sys.path里面找到通向模块文件 ...

  9. 如何查看ffmpeg支持的编码器和封装格式

    查看支持的编码器(也就是-vcodec后面可以接的参数):ffmpeg -codecs 查看支持的封装格式(也就是-f后面可以接的参数):ffmpeg -formats 查看支持的滤镜(也就是-vf后 ...

  10. C++类定义 常量定义

    #include "stdafx.h"#include "iostream" using namespace std; class MyClass{ int _ ...