欢迎、引导

登录、注册

主页

android studio flutter升级版本 flutter升级2.0_flutter

android studio flutter升级版本 flutter升级2.0_App_02

android studio flutter升级版本 flutter升级2.0_flutter_03

环境搭建

可以参考 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,
      ),
    );
  }
}