Flutter:仿京东项目实战(1)

您所在的位置:网站首页 完结鸿蒙 Flutter:仿京东项目实战(1)

Flutter:仿京东项目实战(1)

2023-10-15 22:42| 来源: 网络整理| 查看: 265

在我个人认为学习一门新的语言(快速高效学习) 一定是通过实践,最好的就是做项目,这里我会简单写一个京东的Demo。

Scalfold Widget 是用来描述页面的主结构,一个 MaterialApp 由多个 Scalfold 页面组成,每个 Scalfold 的普遍结果如下:

AppBar:顶部导航栏 body:中间内容体 BottomNavigationBar:底部导航栏

第一天搭建项目框架,实现首页的功能。

第二天实现 分类和商品列表页面: juejin.cn/post/704471…

第三天实现 商品详情页功能实现:juejin.cn/post/704618…

Flutter-混合工程的持续集成实践: juejin.cn/post/704209…

用到的知识点 BottomNavigationBar 底部导航栏基本属性

截屏2021-12-21 上午9.53.18.png

命名路由:关于更多内容可以参考 Flutter-导航与路由堆栈详解

屏幕适配:使用了flutter_screenutil插件,具体有以下这些属性

传入设计稿的px尺寸 px px px ! ScreenUtil().setWidth(540) //根据屏幕宽度适配尺寸 ScreenUtil().setHeight(200) //根据屏幕高度适配尺寸 ScreenUtil().setSp(24) //适配字体 ScreenUtil().setSp(24, allowFontScalingSelf: true) //适配字体(根据系统的“字体大小”辅助选项来进行缩放) ScreenUtil().setSp(24, allowFontScalingSelf: false) //适配字体(不会根据系统的“字体大小”辅助选项来进行缩放) ScreenUtil().pixelRatio //设备的像素密度 ScreenUtil().screenWidth //设备宽度 ScreenUtil().screenHeight //设备高度 ScreenUtil().bottomBarHeight //底部安全区距离,适用于全面屏下面有按键的 ScreenUtil().statusBarHeight //状态栏高度 刘海屏会更高 单位dp ScreenUtil().textScaleFactor //系统字体缩放比例 ScreenUtil().scaleWidth // 实际宽度的dp与设计稿px的比例 ScreenUtil().scaleHeight // 实际高度的dp与设计稿px的比例 网络请求使用 dio 插件实现,详解可以看官网:github.com/flutterchin… import 'package:dio/dio.dart'; void main() async { var dio = Dio(); final response = await dio.get('https://google.com'); print(response.data); }

配置抓包

引入这两个dio 的头文件 import 'package:dio/adapter.dart'; import 'package:dio/dio.dart'; 配置抓包代码 //设置只在debug模式下抓包 final kReleaseMode = false; final Dio dio = Dio(); if (!kReleaseMode){ //设置代理 抓包用 (_dio!.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (HttpClient client) { client.findProxy = (uri) { return "PROXY localhost:8888"; }; //这个是Android手机抓包需要配置的,不然验证证书一直失败 client.badCertificateCallback = (cert, host, port) => true; }; } 轮播图使用 swiper 插件实现

截屏2021-12-20 下午8.51.14.png

SwiperPagination 属性说明:

//如果要将分页指示器放到其他位置,可以修改这个参数 alignment Alignment.bottomCenter 分页指示器与容器边框的距离 margin EdgeInsets.all(10.0) 分页指示器的样式,fraction自定义样式 builder SwiperPagination.dots 页面框架使用 ListView 实现列表

截屏2021-12-20 下午9.33.30.png

商品列表用到了 Wrap ,流式布局、自动换行 属性解析 direction:主轴(mainAxis)的方向,默认为水平。 alignment:主轴方向上的对齐方式,默认为start。 spacing:主轴方向上的间距。 runAlignment:run的对齐方式。run可以理解为新的行或者列,如果是水平方向布局的话,run可以理解为新的一行。 runSpacing:run的间距。 crossAxisAlignment:交叉轴(crossAxis)方向上的对齐方式。 textDirection:文本方向。 verticalDirection:定义了children摆放顺序,默认是down,见Flex相关属性介绍。 项目的搭建

创建 tabs文件夹,里面添加tabs.dart和4个底导首页(home.dart、category.dart、cart.dart、user.dart)。

这里只是贴出主要代码:

bottomNavigationBar: BottomNavigationBar( currentIndex:_currentIndex , onTap: (index){ setState(() { _currentIndex=index; }); }, type:BottomNavigationBarType.fixed , fixedColor:Colors.red, items: [ BottomNavigationBarItem( icon: Icon(Icons.home), label: "首页" ), BottomNavigationBarItem( icon: Icon(Icons.category), label: "分类" ), BottomNavigationBarItem( icon: Icon(Icons.shopping_cart), label: "购物车" ), BottomNavigationBarItem( icon: Icon(Icons.people), label: "我的" ) ], ), 配置命名路由

这里为了便于统一管理,创建一个routers文件夹,在里面创建一个router.dart类来管理路由

//配置路由 final Map routes = { '/': (context) => Tabs(),arguments,), }; //固定写法 var onGenerateRoute = (RouteSettings settings) { final String? name = settings.name; final Function? pageContentBuilder = routes[name]; if (pageContentBuilder != null) { if (settings.arguments != null) { final Route route = MaterialPageRoute( builder: (context) => pageContentBuilder(context, arguments: settings.arguments)); return route; } else { final Route route = MaterialPageRoute(builder: (context) => pageContentBuilder(context)); return route; } } }; 屏幕适配

这里我选择flutter_screenutil实现屏幕适配,flutter_screenutil 默认以750*1334 设计稿为标准 在 pubspec.yaml 添加

flutter_screenutil: ^5.0.0+2

然后点击 Pub get 拉取。创建一个services文件夹,在里面创建screen_adapter.dart类,在里面实现常用的功能

import 'package:flutter_screenutil/flutter_screenutil.dart'; class ScreenAdapter { //宽、高、字号大小转换 static height(num value) { return ScreenUtil().setHeight(value); } static width(num value) { return ScreenUtil().setWidth(value); } static size(num value) { return ScreenUtil().setSp(value); } //获取设备的物理宽度 static getScreenWidth() { return ScreenUtil().screenWidth; } //获取设备的物理高度 static getScreenHeight() { return ScreenUtil().screenHeight; } //状态栏高度 static double statusBarHeight = ScreenUtil().statusBarHeight; //底部安全区域距离 static double bottomBarHeight = ScreenUtil().bottomBarHeight; } 具体实现

Simulator Screen Shot - iPhone 12 Pro - 2021-12-20 at 20.25.29.png

页面整体框架使用 ListView 实现,而内容是分下面三大块实现:

banner区域的轮播图,通过 Swiper 插件实现 猜你喜欢的横向滚动列表,通过 ListView 实现 热门推荐的垂直滚动列表,通过 Wrap 实现 实现banner区域的轮播图

首先引入 flutter_swiper_null_safety: ^1.0.2, 轮播图是网络获取的数据,还需要引入 dio: ^4.0.0 进行网络请求,然后执行 pub get 拉取。

创建轮播图的模型 focus_model.dart,里面的代码实现为

class FocusModel { List result=[]; FocusModel({required this.result}); FocusModel.fromJson(Map json) { if (json['result'] != null) { json['result'].forEach((v) { result.add(new FocusItemModel.fromJson(v)); }); } } Map toJson() { final Map data = new Map(); data['result'] = this.result.map((v) => v.toJson()).toList(); return data; } } class FocusItemModel { String? sId; //可空类型 String? title; String? status; String? pic; String? url; FocusItemModel({this.sId, this.title, this.status, this.pic, this.url}); FocusItemModel.fromJson(Map json) { sId = json['_id']; title = json['title']; status = json['status']; pic = json['pic']; url = json['url']; } Map toJson() { final Map data = new Map(); data['_id'] = this.sId; data['title'] = this.title; data['status'] = this.status; data['pic'] = this.pic; data['url'] = this.url; return data; } } 获取banner数据 //获取热门推荐的数据 _getBestProductData() async { var api = 'https://jdmall.itying.com/api/plist?is_best=1'; var result = await Dio().get(api); var bestProductList = ProductModel.fromJson(result.data); setState(() { this._bestProductList = bestProductList.result; }); } 创建轮播图 Widget _swiperWidget() { if (this._focusData.length > 0) { return Container( child: AspectRatio( aspectRatio: 2 / 1, child: Swiper( itemBuilder: (BuildContext context, int index) { String pic = this._focusData[index].pic; pic = Config.domain + pic.replaceAll('\', '/'); return new Image.network( "${pic}", fit: BoxFit.fill, ); }, itemCount: this._focusData.length, pagination: new SwiperPagination(), autoplay: true), ), ); } else { return Text('加载中...'); } } 具体代码如下: List _focusData = []; @override void initState() { super.initState(); _getFocusData(); } @override Widget build(BuildContext context) { return ListView( children: [ _swiperWidget(), ], ); }

然后运行项目就实现了这样的效果,轮播图的功能做好了

Simulator Screen Shot - iPhone 12 Pro - 2021-12-20 at 20.46.25.png

实现猜你喜欢的效果

封装一个方法,返回一个Widget

Widget _titleWidget(value) { return Container( height: ScreenAdapter.height(32), margin: EdgeInsets.only(left: ScreenAdapter.width(20)), padding: EdgeInsets.only(left: ScreenAdapter.width(20)), decoration: BoxDecoration( border: Border( left: BorderSide( color: Colors.red, width: ScreenAdapter.width(10), ))), child: Text( value, style: TextStyle(color: Colors.black54), ), ); } 获取猜你喜欢网络数据 //获取猜你喜欢的数据 _getHotProductData() async { var api = '${Config.domain}api/plist?is_hot=1'; var result = await Dio().get(api); var hotProductList = ProductModel.fromJson(result.data); setState(() { this._hotProductList = hotProductList.result; }); } 创建猜你喜欢横向列表 //猜你喜欢 Widget _hotProductListWidget() { if (_hotProductList.length > 0) { return Container( height: ScreenAdapter.height(234), padding: EdgeInsets.all(ScreenAdapter.width(20)), child: ListView.builder( //设置滚动方向 scrollDirection: Axis.horizontal, itemBuilder: (contxt, index) { //处理图片 String sPic = _hotProductList[index].sPic; //得到图片URL sPic = Config.domain + sPic.replaceAll('\', '/'); return Column( children: [ Container( height: ScreenAdapter.height(140), width: ScreenAdapter.width(140), margin: EdgeInsets.only(right: ScreenAdapter.width(21)), child: Image.network(sPic, fit: BoxFit.cover), ), Container( padding: EdgeInsets.only(top: ScreenAdapter.height(10)), height: ScreenAdapter.height(44), child: Text( "¥${_hotProductList[index].price}", style: TextStyle(color: Colors.red), ), ) ], ); }, itemCount: _hotProductList.length, ), ); } else { return Text(""); } } 实现效果

截屏2021-12-20 下午9.10.12.png

实现热门推荐功能 获取热门推荐的数据 _getBestProductData() async { var api = '${Config.domain}api/plist?is_best=1'; var result = await Dio().get(api); var bestProductList = ProductModel.fromJson(result.data); setState(() { this._bestProductList = bestProductList.result; }); } 创建商品列表 Widget _recProductListWidget() { var itemWidth = (ScreenAdapter.getScreenWidth() - 30) / 2; return Container( padding: EdgeInsets.all(10), child: Wrap( runSpacing: 10, spacing: 10, children: this._bestProductList.map((value) { //图片 String sPic = value.sPic == null ? '' : value.sPic; sPic = Config.domain+sPic.replaceAll('\', '/'); return Container( padding: EdgeInsets.all(10), width: itemWidth, decoration: BoxDecoration( border: Border.all( color: Color.fromRGBO(233, 233, 233, 0.9), width: 1)), child: Column( children: [ Container( width: double.infinity, child: AspectRatio( //防止服务器返回的图片大小不一致导致高度不一致问题 aspectRatio: 1 / 1, child: Image.network( "${sPic}", fit: BoxFit.cover, ), ), ), Padding( padding: EdgeInsets.only(top: ScreenAdapter.height(20)), child: Text( "${value.title}", maxLines: 2, overflow: TextOverflow.ellipsis, style: TextStyle(color: Colors.black54), ), ), Padding( padding: EdgeInsets.only(top: ScreenAdapter.height(20)), child: Stack( children: [ Align( alignment: Alignment.centerLeft, child: Text( "¥${value.price}", style: TextStyle(color: Colors.red, fontSize: 16), ), ), Align( alignment: Alignment.centerRight, child: Text( "¥${value.oldPrice}", style: TextStyle( color: Colors.black54, fontSize: 14, decoration: TextDecoration.lineThrough)), ) ], ), ) ], ), ); }).toList(), ), ); } 实现效果

截屏2021-12-20 下午9.11.50.png



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3