欢迎、引导 | 登录、注册 | 主页 |
环境搭建
可以参考 https://flutter.cn/docs/get-started/install/macos 搭建
Flutter 使用到的语言为 Dart
Dart 语言学习可以看 https://gitee.com/shizidada/dart-learn
创建项目
// --org com.moose.plus 创建包名
// moose_app 应用名称
flutter create --org com.moose.plus moose_app
moose_app
目录介绍
├── android // android 平台运行代码
│ ├── ...
├── ios // ios 平台运行代码
│ ├── ...
├── lib // flutter 平台运行代码
│ ├── main.dart // 主入口文件
├── test // 测试
│ ├── ...
├── web // web 平台
│ ├── ...
└── pubspec.yaml // flutter 运行依赖
└── ...
开始搭建 Flutter 项目运行脚手架
代码目录
- 在 lib 目录下创建 core 目录,与 UI 无关到逻辑代码
- 在 lib 目录下创建 ui 目录,UI 展示视图代码
├── lib
│ ├── core
│ ├── ui
修改 main.dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'app.dart';
void main() {
// 主入口运行 --> 主要代码 --> app.dart
runApp(ATHApp());
// android 平台 statusBar 沉浸式
if (Platform.isAndroid) {
SystemUiOverlayStyle systemUiOverlayStyle =
SystemUiOverlayStyle(statusBarColor: Colors.transparent);
SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
}
}
添加项目所需依赖
- 国内镜像,访问速度快 https://pub.flutter-io.cn/
dependencies:
flutter:
sdk: flutter
# 构建路由模块
fluro: ^2.0.3
# svg 图片显示, flutter_svg 新版本存在问题,使用临时修复版本,取从 github 上分支,后面换 release 版本
flutter_svg:
git:
url: git://github.com/gskinnerTeam/flutter_svg.git
ref: 12b55b464d2e253f411a17798527a7daa2c00ceb
# 屏幕适配 新版本使用和其他版本有区别
flutter_screenutil: ^4.0.3+3
# 轮播图
flutter_swiper: ^1.1.6
shared_preferences: ^2.0.4
创建 app.dart,主要运行界面
import 'package:flutter/material.dart';
class ATHApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return buildApp(context);
}
Widget buildApp(BuildContext context) {
return MaterialApp(
// 应用在后台挂起时显示标题
title: 'Moose Flutter Learn',
// 修改全局 app 样式
theme: Theme.of(context).copyWith(
appBarTheme: Theme.of(context).appBarTheme.copyWith(
color: kPrimaryColor,
elevation: 0,
brightness: Brightness.light,
)),
// 不现实 debug 标签
debugShowCheckedModeBanner: false,
// 主页
home: ATHSplashScreen());
}
}
应用欢迎界面
...
class ATHSplashScreen extends StatelessWidget {
static final String routeName = "app://splash";
@override
Widget build(BuildContext context) {
return Scaffold(
body: ATHSplashBody(),
);
}
}
....
ATHSplashBody
- 显示一张图片 --> 可能广告
- 倒计时结束或者点击进入 app
class ATHSplashBody extends StatefulWidget {
@override
_ATHSplashBodyState createState() => _ATHSplashBodyState();
}
class _ATHSplashBodyState extends State<ATHSplashBody> {
Timer _timer;
int _count = 5;
@override
void initState() {
super.initState();
_startTime();
}
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
ConstrainedBox(
constraints: BoxConstraints.expand(),
child: Image.asset("assets/images/welcome.png", fit: BoxFit.fill),
),
Positioned(
top: 60,
right: 40,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(8.w)),
child: GestureDetector(
onTap: () {
_navigationPage();
},
child: Container(
color: Colors.black12.withAlpha(200),
width: 180.w,
height: 50.h,
alignment: Alignment.center,
child: Text(
"跳过广告 $_count",
style: TextStyle(color: Colors.white),
),
),
),
),
),
],
);
}
_startTime() async {
var _duration = Duration(seconds: 1);
Timer(_duration, () {
// 空等1秒之后再计时
_timer = Timer.periodic(const Duration(milliseconds: 1000), (v) {
_count--;
if (_count <= 0) {
if (_timer != null) _timer.cancel();
_navigationPage();
} else {
setState(() {});
}
});
return _timer;
});
}
void _navigationPage() async {
_timer.cancel();
// 需要判断是否为第一次进入 app
ATHNavigator.pushFromRight(context, ATHGuideScreen.routeName);
}
}
构建fluro
路由架子
Flutter 可以使用自带的 router‘s,使用 fluro 方便维护管理路由
├── lib
│ ├── core
│ ├── ui
│ ├──├── routers
│ ├──├──├── application.dart
│ ├──├──├── route_handlers.dart
│ ├──├──├── routers.dart
application.dart 定义
- 维护 FluroRouter(其他版本 Router),用于页面跳转
import 'package:fluro/fluro.dart';
class ATHApplication {
static FluroRouter router;
}
route_handlers.dart 定义
- 栗子见代码
- 定义每一个显示的页面
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
Handler splashHandler = Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return ATHSplashScreen();
});
Handler guideHandler = Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {
return ATHGuideScreen();
});
routers.dart 定义具体路由跳转规则
import 'package:fluro/fluro.dart';
import 'route_handlers.dart';
class ATHRoutes {
static void configureRoutes(FluroRouter router) {
router.define(ATHSplashScreen.routeName, handler: splashHandler);
router.define(ATHGuideScreen.routeName, handler: guideHandler);
}
}
使用定义的路由
- 修改 app.dart
- 初始化 FluroRouter
....
ATHApp({Key key}) : super(key: key) {
final router = FluroRouter();
ATHRoutes.configureRoutes(router);
ATHApplication.router = router;
}
....
Widget buildApp() {
home: ATHSplashScreen(),
// 添加挂在到 app 上
onGenerateRoute: ATHApplication.router.generator
}
....
屏幕适配
- 可以获取屏幕像素转换为设计稿对应的尺寸
- 使用
flutter_screenutil: ^4.0.3+3
模块简化操作
使用 flutter_screenutil
- pubspec.yaml 添加 flutter_screenutil 依赖
- 修改 app.dart
@override
Widget build(BuildContext context) {
// ScreenUtilInit 为 flutter_screenutil 提供 API
return ScreenUtilInit(
// 默认设计稿尺寸
designSize: Size(750, 1334),
// 设置是否根据系统字体大小
allowFontScaling: false,
builder: () => buildApp(context));
}
- 在后面使用直接可以使用
栗子
ScreenUtil().setWidth(540) (sdk>=2.6 : 540.w) //根据屏幕宽度适配尺寸
ScreenUtil().setHeight(200) (sdk>=2.6 : 200.h) //根据屏幕高度适配尺寸(一般根据宽度适配即可)
ScreenUtil().radius(200) (sdk>=2.6 : 200.r) //根据宽度或高度中的较小者进行调整
ScreenUtil().setSp(24) (sdk>=2.6 : 24.sp) //适配字体
根据 API 参考 https://github.com/OpenFlutter/flutter_screenutil/blob/master/README_CN.md
应用引导界面
使用 flutter_swiper
- 最后一张引导图显示跳转按钮
class ATHGuideScreen extends StatefulWidget {
static final String routeName = "app://guide";
@override
_ATHGuideScreenState createState() => _ATHGuideScreenState();
}
class _ATHGuideScreenState extends State<ATHGuideScreen> {
List<String> guideImages = [
'assets/images/guide-1.jpeg',
'assets/images/guide-2.jpeg',
];
void _handleCheck() async {
ATHNavigator.pushReplace(context, ATHWelcomeScreen.routeName,
clearStack: true);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Swiper(
itemCount: guideImages.length,
itemBuilder: (BuildContext context, int position) {
String imagePath = guideImages[position];
return Stack(
fit: StackFit.passthrough,
alignment: Alignment.center,
children: [
Image.asset(imagePath, fit: BoxFit.cover),
position == guideImages.length - 1
? Positioned(
bottom: 100.h,
child: TextButton(
onPressed: () {
_handleCheck();
},
style: ButtonStyle(
padding: MaterialStateProperty.all(
EdgeInsets.symmetric(
vertical: 16.h, horizontal: 32.w)),
backgroundColor:
MaterialStateProperty.all(Colors.white)),
child: Text(
"立即体验",
style: TextStyle(color: Colors.black),
),
),
)
: SizedBox()
],
);
},
// 设置页码
pagination: SwiperPagination(
alignment: Alignment.bottomCenter,
builder: DotSwiperPaginationBuilder(
color: Colors.black54, activeColor: Colors.white)),
loop: false,
scrollDirection: Axis.horizontal,
),
);
}
}