使用 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
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()
]
)
)
}
}
// 楼层标题 其实就是一张图片
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()
)
}
}