本系列可能会分好几段,从0开始搭建一个「电商项目」的APP出来。
该 APP 功能的思维导图:
创建项目
我自己用的最新的flutter环境
项目创建好后,删除main.dart无用代码,然后开始创建文件夹:
- api:存放所有数据类
- model:存放所有数据类
- router:存放路由相关
- style:存放所有的公共样式
- utils:存放所有的工具类
- views:存放所有页面
- widgets:存放所有封装好的组件
添加插件
下面我们开始添加依赖。
| 插件 | 作用 |
|---|---|
| flutter_localizations | 国际化 |
| flutter_swiper | 轮播图 |
| dio | 网络请求 |
| shared_preferences | 本地存储 |
| url_launcher | 启动URL的插件 |
| webview_flutter | WebView插件 |
| permission_handler | 于系统权限 |
| image_picker | 选择本地相机图片 |
| cached_network_image | 图片缓存 |
| video_player | 视频播放控件 |
| flutter_spinkit | 加载动画 |
| like_button | 动画点赞 |
| city_pickers | 城市选择器 |
| fluwx | 微信sdk |
| barcode_scan | 扫一扫二维码 |
| photo_view | 图片查看 |
项目共用代码
最常见的莫过于样式通用代码 /style 存放样式有关的通用代码
- 颜色 colors.dart
class Colours {
static const app_main = Color(0xFF4688FA);
static const bg_color = Color(0xfff1f1f1);
}
- 间隔 gaps.dart
class Gaps {
/// 水平间隔
static const Widget hGap4 = const SizedBox(width: 4.0);
/// 垂直间隔
static const Widget vGap2 = const SizedBox(height: 2.0);
}
最后整合起来导入到同一个文件 resources.dart,引用该文件即可,或者按需引入
export 'colors.dart';
export 'gaps.dart';
通用组件(类似前端组件,将一些复用性较强的封装成组件)
例如最基础的加载组件,弱提示组件,二次确认组件;
这里说一下二次确认组件开发的思想,flutter本身提供了showDialog方法,在此基础上进行自己的改造
之前开发过微信小程序,参考微信小程序的showModal思想,该组件提供颜色,文字,成功,失败回调;
void showTipDialog(context, {
String title,
String content,
bool showCancel = true,
String cancelText = '取消',
Color cancelColor = const Color(0xFF000000),
String confirmText = '确定',
Color confirmColor = const Color(0xFF3233F3),
VoidCallback success,
VoidCallback fail,
}) {
showDialog(
// 传入 context
context: context,
// 构建 Dialog 的视图
builder: (_) => Padding(
padding: EdgeInsets.all(40),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(12.0))
),
child: Column(
children: <Widget>[
Gaps.vGap16, /// 横向分割块
title != null ? Padding(
padding: EdgeInsets.only(top: 8, bottom: 8),
child: Text(title, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, decoration: TextDecoration.none, color: Colors.black)),
) : Container(),
Container(
padding: EdgeInsets.only(top: 14, bottom: 30, left: 20, right: 20),
alignment: Alignment.center,
child: Text(content,textAlign: TextAlign.center, style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500, decoration: TextDecoration.none, color: Color(0xFF000000))),
),
Gaps.line,
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Expanded(
child: SizedBox(
height: 48.0,
child: FlatButton(
child: Text(
cancelText,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600
),
),
textColor: cancelColor,
onPressed: (){
Navigator.pop(_);
fail();
},
),
),
),
SizedBox(
height: 48.0,
width: 0.6,
child: DecoratedBox(decoration: BoxDecoration(color: Colours.line)),
),
Expanded(
child: SizedBox(
height: 48.0,
child: FlatButton(
child: Text(
confirmText,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600
),
),
textColor: confirmColor,
onPressed: (){
Navigator.pop(context);
success();
},
),
),
)
],
)
],
),
)
],
),
),
);
}
调用方式(引入组件)
showTipDialog(
context, /// 上下文,直接传build的 context
content: '是否删除该地址?',
confirmText: '删除地址',
success: () {}, /// 成功回调
fail: () {}, /// 失败回调
);
效果
项目截图
图片太多了 先放tab的截图
最后
该篇文章是当前系列的第一篇。 由于还在整理代码,更新之后上github地址