從 Flutter 2.0 開始學 - 實踐、專案搭建

江景發表於2021-03-15
歡迎、引導登入、註冊主頁
從 Flutter 2.0 開始學 - 實踐、專案搭建從 Flutter 2.0 開始學 - 實踐、專案搭建從 Flutter 2.0 開始學 - 實踐、專案搭建

環境搭建

可以參考 flutter.cn/docs/get-st… 搭建

Flutter 使用到的語言為 Dart

Dart 語言學習可以看 gitee.com/shizidada/d…

建立專案

// --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);
  }
}
複製程式碼

新增專案所需依賴

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 參考 github.com/OpenFlutter…

應用引導介面

使用 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,
      ),
    );
  }
}
複製程式碼

從 Flutter 2.0 開始學 - Widget 元件化

從 Flutter 2.0 開始學 - 路由封裝、介面跳轉、登入註冊 UI

相關文章