flutter电商实战-首页的实现

使用 dio 库拿到数据 在 pubspec.yaml 文件里面引入:

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^0.1.2
  dio: ^3.0.8 #引入 dio 库
  flutter_swiper: ^1.1.6 #轮播图
  flutter_screenutil: ^1.0.1 #适配屏幕
  url_launcher: ^5.4.1 #跳转
  flutter_easyrefresh: ^2.0.8 #上拉加载下拉刷新

在lib目录下新建一个文件夹config,然后新增一个service_url.dart

// service_url.dart
const serviceUrl= 'http://xxxx'
const servicePath = {
  'homePageContent': serviceUrl+'wxmini/homePageContent', // 商店首页信息
  'homePageBelowConten': serviceUrl+'wxmini/homePageBelowConten', // 商店首页火爆专区商品
  'getCategory': serviceUrl+'wxmini/getCategory', // 商品类别信息
};

然后lib目录下新增一个service文件夹,往里面新增一个 service_method.dart 文件:

// service_method.dart
// 封装一个 dio 的请求方法
import "package:dio/dio.dart";
import 'dart:async';
import '../config/service_url.dart';
Future request(url,{formdata}) async{
  try{
    print('开始首页数据...............');
    Response response;
    Dio dio = new Dio();
    // dio.options.contentType=ContentType.parse("application/x-www-form-urlencoded") as String;
    dio.options.contentType= Headers.formUrlEncodedContentType;
    // 判断是否有参数
    if(formdata == null){
      response = await dio.post(servicePath[url]);
    }else{
      // var formData = {'lon':'115.02932','lat':'35.76189'};
      response = await dio.post(servicePath[url],data:formdata); // data 里面就是请求过去的参数
    }
    
    if(response.statusCode==200){
      // print(response.data);
      return response.data;
    }else{
      throw Exception('后端接口出现异常,请检测代码和服务器情况.........');
    }
  }catch(e){
    return print('ERROR:======>$e');
  }
}

首页的实现()

import 'package:flutter/material.dart';
import '../service/service_method.dart';
import 'package:flutter_swiper/flutter_swiper.dart'; // 轮播图
import 'package:flutter_screenutil/flutter_screenutil.dart'; // 适配屏幕
import 'package:url_launcher/url_launcher.dart'; // 跳转到拨打电话的页面 网页 短信都行
import 'package:flutter_easyrefresh/easy_refresh.dart'; // 上拉刷新 下拉加载

class HomePage extends StatefulWidget{
  HomePage({Key key}) : super(key: key);
  @override
  _HomePageState createState() => _HomePageState();
}
/*
使用混入保持住页面的状态: 使用混入 AutomaticKeepAliveClientMixin
1.必须是 StatefulWidget 才可使用 记住页面这个操作
    重写 @override bool get wantKeepAlive => true;
2.在首页 可以使用
    IndexedStack(
        index: this._currentIndex, // 当前页面的索引
        children: this._list, // 对应的页面
    ),就可以让我们页面有缓存的效果了
*/
class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin{
	@override
	bool get wantKeepAlive = true; // 重写这个方法 
	// 定义页数 上拉加载更多
	int page = 1;
	// 定义商品列表
	List<Map> hotGoodsList = [];
	// 组件的 标题
	Widget hotTile = Container(
		margin: EdgeInsets.only(top: 10, bottom: 5), // 标题的上下编剧
		alignment: Alignment.center,
		color: Colors.transparent,
		child: Text('火爆专区')
	);
	// 具体的商品列表布局
	Widget _wrapList(){
		// 一定要进行判断 否则这个流布局会报错
		if (this.hotGoodsList.length != 0) {
      // map型数组 转换成 组件型 数组展示
      List<Widget> listWidget = this.hotGoodsList.map((val) {
        return InkWell(
          onTap: () {
            print('火爆专区类型');
          },
          child: Container(
            width: ScreenUtil().setWidth(372),
            color: Colors.white,
            padding: EdgeInsets.all(5),
            margin: EdgeInsets.only(bottom: 3),
            child: Column(
              children: <Widget>[
                Image.network(val['image'], width: ScreenUtil().setWidth(370)),
                Text(
                  val['name'],
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                  style: TextStyle(
                      color: Colors.pink, fontSize: ScreenUtil().setSp(26)),
                ),
                Row(
                  children: <Widget>[
                    Text('¥${val['mallPrice']}'),
                    Text('¥${val['price']}',
                        style: TextStyle(
                            color: Colors.black26,
                            decoration: TextDecoration.lineThrough))
                  ],
                )
              ],
            ),
          ),
        );
      }).toList();
      // wrap 布局会自动换行 当宽度超过的时候
      return Wrap(
        spacing: 2, // 主轴的间距
        children: listWidget,
        // children: [],
      );
    } else {
      return Text('');
    }
	}
	// 组合标题和商品
	Widget _hotGroup(){
		return Container(
		child: Cloumn(
			children: <Widget>[hotTile, _wrapList()]
		)
		)
	}

	Widget build(BuildContext context){
	return Scaffold(
		appBar: AppBar(
		title: Text("百姓生活+")
		),
		// FutureBuilder 会自动发送异步的请求
		body: FutureBuilder(
			// 请求首页的数据
			future: request('homePageContent',formdata: {'lon': '115.02932', 'lat': '35.76189'}),
			// snapshot 表示从后台异步拿到的数据
			builder: (context,snapshot){
				// snapshot.hasData 表示有数据
				if(snapshot.hasData){
					var data = json.decode(snapshot.data.toString());
					var datas = data['data'];
					// 轮播图
					List<Map> swiper = (datas['slides'] as List).cast();
					// 九宫格
					List<Map> navgatorList = (datas['category'] as List).cast();
					// 广告
					String adBannerUrl = datas['advertesPicture']['PICTURE_ADDRESS'];
					// 店长图片的图片
					String leaderImage = datas['shopInfo']['leaderImage'];
					// 店长的电话 通过第三方插件来拨打电话
					String leaderPhone = datas['shopInfo']['leaderPhone'];
					// 商品推荐
					List<Map> recommondList = (datas['recommond'] as List).cast();
					// 楼层布局图片
					String titleAddress = datas['floor1Pic']['PICTURE_ADDRESS'];
					// 下面的楼层布局
					List<Map> goodsItem = (datas['floor1'] as List).cast();
					List<Map> goodsItem2 = (datas['floor2'] as List).cast();
					List<Map> goodsItem3 = (datas['floor3'] as List).cast();
					// 上拉加载更多 里面一定要使用 ListView
					return EasyRefresh(
						child: ListView(
							children: <Widget>[
							// 这边就会执行相关的widget组件 会执行下面
								MySwiper(imglist: swiper),
                      			TopBar(navigatorList: navgatorList),
                      			AdBanner(adBannerUrl: adBannerUrl),
                      			CallPhone(leaderImage: leaderImage, leaderPhone: leaderPhone),
                      			Recommed(recommondList: recommondList),
                      			FloorTitle(titleAddress: titleAddress),
                      			FloorGoods(goodsItem: goodsItem),
                      			FloorGoods(goodsItem: goodsItem2),
                      			FloorGoods(goodsItem: goodsItem3),
								_hotGroup(), // 火爆商品区
							]
						),
						// 上拉加载更多 监听上拉加载的事件 上拉就会触发这个事件
						footer: classiaclFooter( // 配置
							bgColor: Colors.white,
                    		textColor: Colors.pink,
                    		loadReadyText:"上拉加载更多",
                    		loadingText: "加载中...",
                    		loadFailedText: "加载失败",
                    		loadedText: "加载成功",
                    		noMoreText: "",
                    		showInfo: false
						),
						onLoad: () async {
							await Future.delayed(Duration(second: 1), (){
								// 定义一个页码的分页
								var formdata = {'page':this.page};
								request('homePageBelowConten',formdata:formdata).then((res){
									// 转换成 map 集合类型
									var data = json.decode(res.toString());
									// 存放到新的
									List<Map> newGoodsList = (data['data'] as List).cast();
									setState({
										// 添加数组到 hotGoodsList
										this.hotGoodsList.addAll(newGoodsList);
										// 分页的页数
										this.page++;
									})
								})
							})
						}else{
							return Center(
								child: Text("加载中...")
							)
						}
					)
				}
			}
		)
	)
	}
}

相关的代码如下

轮播图
// 轮播图
class MySwiper extends StatelessWidget{
	List imgList = [];
	MySwiper({this.imgList}); // 构造函数
	@override
	Widget build(BuildContent context){
		return Container(
			// 因为在首页定义了 ScreenUtil.init(context, width: 750, height: 1334);
			height: ScreenUtil().setHeight(333),
			width: ScreenUtil().setWidth(750),
			child: Swiper(
				itemBuilder: (BuildContext context,int index){
					return new Image.network(
						this.imglist[index]['image'],
						fit:BoxFit.fill
					)
				},
				// 数组的长度
				itemCount: this.imglist.length,
				// 控制分液器的
				pagination: new SwiperPagination(), 
				loop: true,
				autoplay: true
			)
		)
	}
}
首页的九宫格
class TopBar extends StatelessWidget{
	final List navigatorList;
	const TopBar({Key key,this.navigatorList}) : super(key:key);
	Widget _gridViewItemUi(){
		return InkWell(
			onTab: (){},
			child: Column(
				children: <Widget>[
				Image.network(item['image']),
				Text(item('mallCategoryName'))
				]
			)
		)
	}
	@override
	Widget build(BuildContext context){
		// 删除第十一个 只显示10个
		if(this.navigatorList.length > 10){
			this.navigatorList.removeRange(10,this.navigatorList.length);
		}
		return Container(
			height: ScreenUtil().setHeight(320), // 设置这一整个九宫格的高度为 320
			padding: EdgeInsets.all(3.0),
			child: GridView.count(
				// 把GridView 的滚动时间也清除掉
				physics: NeverScrollableScrollPhysics(),
				crossAxisCount: 5, // 表示设置一行值显示5列
				padding: EdgeInset.all(5.0),
				children: this.navigatorList.map((item){
					return _gridViewItemUi(context,item)
				}).toList();
			)
		)
	}
}
广告的banner
// 广告 banner
class AdBanner extends StatelessWidget{
	final String adBannerUrl;
	const AdBanner({key: key, this.adBannerUrl}):super(key:key);
	@override
	Widget build(BuildContext context){
		return Container(
			child: Image.network(adBannerUrl)
		)
	}
}
店长图片以及拨打店长电话
class CallPhone extends StatelessWidget {
	final String leaderImage; // 图片
	final String leaderPhone; // 手机号码
	CallPhone({Key:key, this.leaderImage, this.leaderPhone}):super(key:key); // 构造函数
	// 点击拨打店长的电话 这边使用的插件
	_launchUrl() async{
		String url = 'tel:'+this.leaderPhone;
		if(await canLaunch(url)){
			await launch(url);
		}else{
			throw '您拨打的电话不合法';
		}
	}
	@override
	Widget build(BuildContext context){
		return Container(
			child: InkWell(
				onTap: this._launchUrl,
				child: Image.network(leaderImage)
			)
		)
	}
}
商品推荐
class Recommed extends StatelessWidget{
	final List recommondList;
	Recomed({Key:key, this.recommondList}):super(key:key);
	// 标题方法
	Widget _titleWidget(){
		return Container(
			alignment: Alignment.centerLeft,
			padding: EdgeInset.fromLTRB(10,2,0,0),
			decoration: BoxDecoration(
				color: Colors.white,
				border: Border(bottom: BorderSide(width: 0.5,color: Colors.black12))
			),
			child:Text('商品推荐',style: TextStyle(color: Colors.pink)),
		)
	}
	// 商品单独项方法
	Widget _item(index){
		return InkWell(
			onTap: (){},
			child: Container(
				height: ScreenUtil().setHeight(330),
				width: ScreenUtil().setWidth(250), // 250 * 3 = 750
				padding: EdgeInsets.fromLTRB(10,10,10,0), 
				decoration: BoxDecoration(
					color: Colors.white,
					border: Border(left: BorderSide(width: 0.5, color: colors.black12))
				),
				child: Column(
					children: <Widget>[
						Image.network("¥${recommondList[index]['image']}"),
						Text("¥${recommondList[index]['mallPrice']}"),
						Text("¥${recommondList[index]['price']}",
						style: TextStyle(
							decoration: TextDecoration.lineThrough, // 删除线
							color: Colors.grey
						)
						)
					]
				)
			)
		)
	}
	// 组合成整个横向列表滚动
	Widget _recommedList(){
		return Container(
			height: ScreenUtil().setHeight(330),
			child: ListView.builder(
				scrollDirection: Axis.horizontal, // 设置为横向滚动
				itemCount: this.recommondList.length,
				itemBulder: (context,index){ // 上下文 索引
					return _item(index)
				}
			)
		)
	}
	@override
	Widget build(BuildContext context){
		return Container(
			height: ScreenUtil().setHeight(380),
			margin: EdgeInsets.only(top: 10.0),
			child: Column(
				children: <Widget>[
					_titleWidget(),
					_recommondList()
				]
			)
		)
	}
}
楼层Widget 布局
// 楼层标题 其实就是一张图片
class FloorTitle extends StatelessWidget{
	final String titleAddress;
	// 构造方法
	FloorTitle({Key:key, this.titleAddress}):super(key:key);
	@override
	Widget build(BuildContext context){
		return Container(
			padding: EdgeInsets.all(8),
			child: Image.network(titleAddress)
		)
	}
}
// 楼层widget布局
class FloorGoods extends StatelessWidget{
	final List<Map> goodsItem;
	FloorGoods((Key:key,this.goodsItem)):super(key:key);
	// 单个商品展示
	Widget _good(Map goods){
		return Conatiner(
			width: ScreendUtil().setWidth(375),
			child: InkWell(
				onTap:(){},
				child: Image.network(goods['image']),
			)
		)
	}
	// 第一层的楼层布局显示
	Widget _goodFirst(){
		return Row(
			children: <Widget>[
				_good(goodsItem[0]),
				Column(
					children: <Widget>[
						_good(goodsItem[1]),
						_good(goodsItem[2]),
					]
				)
			]
		)
	}
	// 第二层楼层的布局显示
	Widget _goodSecond(){
		return Row(
			children: <Widget>[
				_good(goodsItem[3]),
				_good(goodsItem[4]),
			]
		)
	}
	// 最后在组装数据
	Widget _goodList(){
		return Column(
			children: <Widget>[
				_goodFirst(),
				_goodSecond()
			]
		)
	},
	@override
	Widget build(BuildContext context){
		return Container(
			child: _goodsList()
		)
	}
}

你可能感兴趣的:(flutter&dart)