Flutter学习记录——23.实现一个类似淘宝的商品展示页面

爱被打了一巴掌 2023-06-19 11:03 43阅读 0赞

文章目录

  • 1.知识整理
  • 2.应用编写
    • 2.1 应用编写目标
    • 2.2 应用浏览
    • 2.3 应用分析
    • 2.4 应用实现
  • 3.总结

经过前面两大部分的详细讲解,相信大家对大部分的布局方式、组件的使用、逻辑业务编写都有了很深入的了解,那么接下来我们就用前面学习的一些知识来进行一个实践:实现一个淘宝风格的商品展示列表,通过这个实例我们可以复习巩固我们之前学过的知识,也算是一个总结与检验。本课练习篇主要是将通过一些组件、自定义组件、常用布局等知识点来完成一个淘宝风格的商品展示列表,一起来学习吧,很简单!

1.知识整理

在进行案例编写前,我们先整理下我们前面学习过的 Flutter 相关 Widget:

  • 基础组件(Text、Image、Button)
  • 基础组件(AppBar、AlertDialog、Icon)
  • 基础组件(TextField、Form表单)
  • 基础布局(Scaffold、Container、Center)
  • 基础布局(Row、Column、Flex、Expanded、Stack、IndexedStack)
  • 列表滚动组件(CustomScrollView、ListView、ScrollView、ExpansionPanel)
  • 导航组件(TabBar、NavigationBar、PageView)
  • 流式布局组件(Flow、Wrap)
  • 表格组件(Table、Data Tables)
  • 自定义组件

那么我们这节实践课,就通过以上我们学过的一些 Widget 和技术来布局一个淘宝商品列表的应用页面,练练手,也对之前的知识加深一下印象。

2.应用编写

2.1 应用编写目标

本节博客将用前面所学的一些布局 Widget 和组件 Widget 来编写一个淘宝商品列表的应用页面。

2.2 应用浏览

在这里插入图片描述

2.3 应用分析

里面涉及到: Scaffold、Container、Row、Column、TextField、AppBar、Text、Image、FloatingActionButton、Icon、ClipRRect、CustomScrollView、SliverPersistentHeader、TabBar、TabBarView 等。

首先分析下这个页面,我们主要是进行 Item 页面的绘制和顶部 Tab 页的效果绘制。我们这里可以使用 Scaffold 构建页面布局框架,然后使用 TabBar 实现顶部的 Tab 页效果。TabBar 的切换页面的 body 显示部分,使用TabBarView 实现。

Item 的布局结构部分,我们通过效果图可以看出,外层可以使用 Column 纵向线性布局 Widget,图片圆角部分处理美化,使用 ClipRRect 和 BoxDecoration 进行圆角处理。
在这里插入图片描述

2.4 应用实现

代码实现:

  1. import 'package:flutter/material.dart';
  2. import 'package:flutter/rendering.dart';
  3. /// 实现一个淘宝风格的商品展示列表
  4. class PracticeTwoSamples extends StatefulWidget {
  5. @override
  6. State<StatefulWidget> createState() {
  7. return PracticeTwoSamplesState();
  8. }
  9. }
  10. class PracticeTwoSamplesState extends State<PracticeTwoSamples>
  11. with SingleTickerProviderStateMixin {
  12. // 页面切换TabController
  13. TabController _tabController;
  14. @override
  15. void initState() {
  16. super.initState();
  17. _tabController = TabController(initialIndex: 0, length: 4, vsync: this);
  18. }
  19. @override
  20. Widget build(BuildContext context) {
  21. return Scaffold(
  22. // 定义顶部标题栏
  23. appBar: AppBar(
  24. primary: true,
  25. elevation: 0,
  26. automaticallyImplyLeading: true,
  27. title: Container(
  28. padding: EdgeInsets.only(left: 0, right: 0, top: 10, bottom: 10),
  29. child: TextField(
  30. maxLines: 1,
  31. autofocus: false,
  32. // TextFiled装饰
  33. decoration: InputDecoration(
  34. filled: true,
  35. contentPadding: EdgeInsets.all(10),
  36. fillColor: Colors.white,
  37. border: OutlineInputBorder(
  38. borderSide: BorderSide.none,
  39. gapPadding: 0,
  40. borderRadius: BorderRadius.all(Radius.circular(20))),
  41. hintText: '衬衫男',
  42. suffixIcon: Icon(Icons.photo_camera)),
  43. ),
  44. ),
  45. centerTitle: true,
  46. // 右侧收起的更多按钮菜单
  47. actions: <Widget>[
  48. PopupMenuButton(
  49. itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
  50. PopupMenuItem<String>(
  51. child: Text("消息"),
  52. value: "message",
  53. ),
  54. PopupMenuItem<String>(
  55. child: Text("分享"),
  56. value: "share",
  57. ),
  58. ],
  59. onSelected: (String action) {
  60. switch (action) {
  61. case "message":
  62. print("message");
  63. break;
  64. case "share":
  65. print("share");
  66. break;
  67. }
  68. },
  69. onCanceled: () {
  70. print("onCanceled");
  71. },
  72. )
  73. ],
  74. // 紧挨着标题栏AppBar的TabBar
  75. bottom: TabBar(
  76. controller: _tabController,
  77. isScrollable: false,
  78. // 标签选中颜色
  79. labelColor: Color.fromRGBO(247, 70, 0, 1),
  80. unselectedLabelColor: Colors.black,
  81. indicatorColor: Color.fromRGBO(247, 70, 0, 1),
  82. indicatorSize: TabBarIndicatorSize.label,
  83. // 几个Tab按钮
  84. tabs: <Widget>[
  85. Tab(
  86. text: "全部",
  87. ),
  88. Tab(
  89. text: "天猫",
  90. ),
  91. Tab(
  92. text: "店铺",
  93. ),
  94. Tab(
  95. text: "淘宝经验",
  96. ),
  97. ],
  98. ),
  99. ),
  100. // 右下角悬浮的按钮Button
  101. floatingActionButton: FloatingActionButton(
  102. backgroundColor: Colors.white,
  103. onPressed: () { },
  104. mini: true,
  105. elevation: 1,
  106. highlightElevation: 2,
  107. child: Icon(
  108. Icons.vertical_align_top,
  109. ),
  110. ),
  111. // 主体部分布局内容,使用了TabBarView
  112. body: TabBarView(
  113. controller: _tabController,
  114. // 内部切换页布局内容
  115. children: <Widget>[
  116. getPage1(),
  117. getPage1(),
  118. Center(
  119. child: Text("data3"),
  120. ),
  121. Center(
  122. child: Text("data4"),
  123. ),
  124. ],
  125. ),
  126. );
  127. }
  128. // 将页面布局单独提取出来写,方便
  129. Widget getPage1() {
  130. // 建议最外层使用Container包裹一层
  131. return Container(
  132. padding: EdgeInsets.all(10),
  133. decoration: BoxDecoration(
  134. color: Colors.white,
  135. borderRadius: BorderRadius.only(
  136. topLeft: Radius.circular(30), topRight: Radius.circular(30))),
  137. // 内部页面使用CustomScrollView来实现滚动效果
  138. child: CustomScrollView(slivers: <Widget>[
  139. // 放置一个可推上去的顶部的标题栏
  140. SliverPersistentHeader(
  141. floating: true,
  142. delegate: _SliverAppBarDelegate(
  143. maxHeight: 30,
  144. minHeight: 30,
  145. child: Container(
  146. height: 30,
  147. color: Colors.white,
  148. alignment: Alignment.center,
  149. child: Text('淘宝购物悬浮Header'),
  150. )),
  151. ),
  152. // 放置一个固定的顶部的标题栏
  153. SliverPersistentHeader(
  154. pinned: true,
  155. delegate: _SliverAppBarDelegate(
  156. maxHeight: 30,
  157. minHeight: 30,
  158. child: Container(
  159. color: Colors.white,
  160. padding: EdgeInsets.all(5),
  161. height: 30,
  162. child: Row(
  163. mainAxisAlignment: MainAxisAlignment.spaceAround,
  164. children: <Widget>[
  165. Row(
  166. children: <Widget>[
  167. Text(
  168. '综合',
  169. style: TextStyle(color: Colors.orange),
  170. ),
  171. Icon(
  172. Icons.arrow_drop_down,
  173. color: Colors.orange,
  174. ),
  175. ],
  176. ),
  177. Text(
  178. '销量',
  179. style: TextStyle(color: Colors.black),
  180. ),
  181. Row(
  182. children: <Widget>[
  183. Text(
  184. '筛选 ',
  185. style: TextStyle(color: Colors.black),
  186. ),
  187. Icon(
  188. Icons.filter_vintage,
  189. color: Colors.black,
  190. size: 16,
  191. ),
  192. ],
  193. ),
  194. ],
  195. ),
  196. )),
  197. ),
  198. // 列表内容,使用SliverList实现
  199. SliverList(
  200. delegate:
  201. SliverChildBuilderDelegate((BuildContext context, int index) {
  202. return Container(
  203. alignment: Alignment.center,
  204. // 每条内容的布局Item
  205. child: getItem(),
  206. );
  207. },
  208. // 定义了60条Item数据
  209. childCount: 60),
  210. )
  211. ]),
  212. );
  213. }
  214. // 每条内容的布局Item
  215. Widget getItem() {
  216. return Container(
  217. padding: EdgeInsets.all(5),
  218. child: Row(children: <Widget>[
  219. // 圆角图片
  220. ClipRRect(
  221. borderRadius: BorderRadius.circular(10.0),
  222. child: Image.network(
  223. 'https://g-search2.alicdn.com/img/bao/uploaded/i4/i4/778081993/O1CN01R7Ytfe1QapseIzl8o_!!778081993.jpg_250x250.jpg_.webp',
  224. height: 108,
  225. ),
  226. ),
  227. // 用SizedBox增加间距
  228. SizedBox(
  229. width: 10,
  230. ),
  231. // 右侧的商品描述信息
  232. Column(
  233. crossAxisAlignment: CrossAxisAlignment.start,
  234. children: <Widget>[
  235. SizedBox(
  236. height: 6,
  237. ),
  238. Row(
  239. children: <Widget>[
  240. // 天猫的标签实现
  241. Container(
  242. padding:
  243. EdgeInsets.only(left: 1, right: 1, top: 0, bottom: 0),
  244. decoration: BoxDecoration(
  245. color: Colors.red,
  246. border:
  247. Border.all(color: Color(0xFFFF0000), width: 0.5),
  248. borderRadius: BorderRadius.all(Radius.circular(5))),
  249. child: Text(
  250. '天猫',
  251. style: TextStyle(
  252. color: Colors.white,
  253. fontSize: 10,
  254. ),
  255. ),
  256. ),
  257. // 商品标题
  258. Text(
  259. ' 夏季格子男士韩版修身薄款休闲棉衬衣 ',
  260. style: TextStyle(
  261. color: Colors.black,
  262. ),
  263. maxLines: 2,
  264. softWrap: true,
  265. )
  266. ],
  267. ),
  268. SizedBox(
  269. height: 3,
  270. ),
  271. // 商品特征属性
  272. Text(
  273. '格子布面料 | 方领 | 薄面料',
  274. style: TextStyle(color: Colors.grey, fontSize: 12),
  275. ),
  276. SizedBox(
  277. height: 3,
  278. ),
  279. // 两个横向标签
  280. Row(
  281. children: <Widget>[
  282. Container(
  283. padding:
  284. EdgeInsets.only(left: 3, right: 3, top: 1, bottom: 1),
  285. decoration: BoxDecoration(
  286. border:
  287. Border.all(color: Color(0xFFFF0000), width: 0.5),
  288. borderRadius: BorderRadius.all(Radius.circular(5))),
  289. child: Text(
  290. '天猫无忧购',
  291. style: TextStyle(
  292. color: Colors.red,
  293. fontSize: 10,
  294. ),
  295. ),
  296. ),
  297. SizedBox(
  298. width: 10,
  299. ),
  300. Container(
  301. padding:
  302. EdgeInsets.only(left: 3, right: 3, top: 1, bottom: 1),
  303. decoration: BoxDecoration(
  304. border: Border.all(color: Colors.yellow, width: 0.5),
  305. borderRadius: BorderRadius.all(Radius.circular(5))),
  306. child: Text(
  307. '包邮',
  308. style: TextStyle(
  309. color: Colors.yellow,
  310. fontSize: 10,
  311. ),
  312. ),
  313. )
  314. ],
  315. ),
  316. SizedBox(
  317. height: 3,
  318. ),
  319. // 价格信息
  320. Row(
  321. crossAxisAlignment: CrossAxisAlignment.end,
  322. children: <Widget>[
  323. Text(
  324. '¥',
  325. style: TextStyle(color: Colors.orange, fontSize: 12),
  326. ),
  327. Text(
  328. '78',
  329. style: TextStyle(color: Colors.orange, fontSize: 20),
  330. ),
  331. SizedBox(
  332. width: 10,
  333. ),
  334. Text(
  335. '530人付款 杭州',
  336. style: TextStyle(color: Colors.grey, fontSize: 12),
  337. ),
  338. ],
  339. ),
  340. SizedBox(
  341. height: 2,
  342. ),
  343. // Item底部店铺信息
  344. Row(
  345. crossAxisAlignment: CrossAxisAlignment.end,
  346. children: <Widget>[
  347. Text(
  348. '哥尼诺旗舰店',
  349. style: TextStyle(color: Colors.grey, fontSize: 12),
  350. ),
  351. Text(
  352. ' 进店 >',
  353. style: TextStyle(color: Colors.black, fontSize: 12),
  354. ),
  355. SizedBox(
  356. width: 10,
  357. ),
  358. Icon(
  359. Icons.more_horiz,
  360. color: Colors.grey,
  361. size: 20,
  362. ),
  363. ],
  364. )
  365. ],
  366. )
  367. ]));
  368. }
  369. }
  370. // SliverPersistentHeader的SliverPersistentHeaderDelegate实现
  371. class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  372. _SliverAppBarDelegate({
  373. @required this.minHeight,
  374. @required this.maxHeight,
  375. @required this.child,
  376. });
  377. final double minHeight;
  378. final double maxHeight;
  379. final Widget child;
  380. @override
  381. double get minExtent => minHeight;
  382. @override
  383. double get maxExtent => maxHeight;
  384. @override
  385. Widget build(
  386. BuildContext context, double shrinkOffset, bool overlapsContent) {
  387. return child;
  388. }
  389. @override
  390. bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
  391. return maxHeight != oldDelegate.maxHeight ||
  392. minHeight != oldDelegate.minHeight ||
  393. child != oldDelegate.child;
  394. }
  395. }

3.总结

这样就实现了一个淘宝风格的商品展示列表,涵盖了我们前面所学习的一些 Widget。相信通过这样一个综合实例,大家可以对 Flutter 的页面绘制、应用开发的学习有一个总结。

也可以在这个 Flutter 案例网站进行学习和查看、仿写:https://itsallwidgets.com/

本节博客先给大家总结了前面所学的知识,再通过实践案例来检查和巩固之前学到的这些 Widget 和布局相关内容。主要注意点和建议如下:

  • 熟练掌握这里面涉及到的 Widget 用法,都是常用的、比较重要的,对其中 Widget 的细节用法一定要学会举一反三和自己扩展学习理解,只有在项目实践中,才会更深入、更快地巩固知识。
  • 将本节博客内容动手敲一遍,看是否遇到了什么问题,然后尝试去解决;

发表评论

表情:
评论列表 (有 0 条评论,43人围观)

还没有评论,来说两句吧...

相关阅读