Flutter启动配置插件flutter_boot的使用

Flutter启动配置插件flutter_boot的使用

快速开发Flutter App 的MVI框架、封装Dio网络请求及便捷工具。

功能

  • 对生命周期感知的网络请求,基于Dio 封装。
  • LiveData、ViewModel等实现 MVI
  • Screen_Util及方法拓展(屏幕适配代码来自:flutter_screenutil)。
  • EventBus
  • InvokeController 组件间状态提升
  • 组件
    • LayoutOnCreated :首帧渲染完成回调
    • OverlayTier : 基于OverlayEntry的弹出层,可设置超时时间。可制作toast 与 弹框。
    • FadeEffect: 点击组件时有变淡效果。

示例图

开始

pubspec.yaml 文件中添加插件:

flutter pub add flutter_boot

LiveData

可观察状态变动:

// 定义待观察的状态
var liveCounter = LiveData.useState(0);

// 使用
liveCounter.watch((count) => Text('$count'));

// 更新状态
liveCounter.setState((v) {
  liveCounter.value = v + 1;
});

ViewModel

配合LiveData用于观察多个状态变化:

// 可观察多个状态变化,如果仅观察一个,可使用 LiveDataBuilder
[viewModel.stateCounter].watch((){
  var counterValue = viewModel.stateCounter.value;
  return Column(
    children: [
      Text(
        // 计数
        '${counterValue.num}',
        style: Theme.of(context)
                .textTheme
                .headlineMedium
                ?.copyWith(color: counterValue.color),
      ),
    ],
  );
}),

Widget 链式调用

链式调用,可减少嵌套。原理是使用拓展方法,用组件包裹当前组件、组件方法命名一般是组件类名的小写。

Expanded(
  flex: 1,
  child: GestureDetector(
    onTap: () => logI("----点击"),
    child: const Padding(
      padding: EdgeInsets.all(8),
      child: Text("假如我是文本按钮"),
    ),
  ),
),

24.verticalSpace, // 等同 SizedBox(height: 24,),

// 链式调用,减少嵌套。
"假如我是文本按钮"
    .text()
    .padding(const EdgeInsets.all(8))
    .gestureDetector(onTap: () => logI("----点击"))
    .expanded(1),

基础网络请求

基于Dio封装的网络请求,简化请求参数:

void _clickRequest() async {
  showLoadingDialog(context);
  // 哔哩哔哩每周必看
  var param = Param.url("https://tenapi.cn/v2/{id}")
          .tie("num", 120, type: ParamType.query) // query 参数
          .tie("id", "weekly", type: ParamType.path); // path 参数

  // 网络请求
  AnHttp.anHttp<String>(param, method: HttpMethodType.get).then((value) {
    var jsonMap = jsonDecode(value.data!);
    var bili = BaseModel<BiliBili>.fromJson(
            jsonMap, (json) => BiliBili.fromJson(json));

    if (bili.data != null) {
      setState(() {
        recordCount = bili.data?.list.length ?? 0;
      });
    }
    dismissLoadingDialog(context);
  }).catchError((err) {
    print("------err:${err}");
  });
}

MVI 实现

参考样例:

class _HttpViewModelPageState extends State<HttpViewModelPage>
    with ViewModelScope {

  late var viewModel = HttpPageViewModel();
  var scrollerController = ScrollController();

  // ViewModel 初始化
  [@override](/user/override)
  List<ViewModel> useViewModels() {
    return [viewModel];
  }
  
  Future _onRefresh() async {
    viewModel.currentPage = 1;
    return viewModel.loadBiliBili();
  }
  // ...

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: RefreshIndicator(
          key: _refreshKey,
          onRefresh: _onRefresh,
          child: LiveDataBuilder(
              observe: viewModel.recordState, // 监听状态
              builder: (context, value, child) {
                var records = viewModel.recordState.value;
                return ListView.separated(...);
              })),
    );
  }

  Widget itemWidget(BuildContext context, BiliBiliRecord item, bool last) {
    return Container(...);
  }
}

示例代码

以下是完整的示例代码:

import 'package:example/event_bus_page.dart';
import 'package:example/hint_toast_loading_page.dart';
import 'package:example/http_view_model_page.dart';
import 'package:example/invoke_controller_page.dart';
import 'package:example/live_counter_page.dart';
import 'package:example/widget_chain_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_boot/boot.dart';
import 'package:flutter_boot/kits.dart';

import 'base_http_page.dart';
import 'live_view_model_page.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  [@override](/user/override)
  Widget build(BuildContext context) {
    systemEnabledUiMode(
        mode: SystemUiMode.manual,
        overlays: [SystemUiOverlay.top]).then((value) {
      SystemUiOverlayStyle style = SystemUiOverlayStyle.light.copyWith(
        statusBarColor: Colors.transparent,
        // 状态栏颜色
        systemNavigationBarColor: Colors.transparent,
        // 导航栏颜色
        systemNavigationBarIconBrightness: Brightness.dark,
        statusBarIconBrightness: Brightness.dark,
        statusBarBrightness: Brightness.dark,
      );
      systemUIOverlayStyle(style);
    });
    return BootScope(
      builder: (context, child) {
        // 字体不跟随系统大小
        return MediaQuery(
            data: MediaQuery.of(context)
                .copyWith(textScaler: TextScaler.noScaling),
            child: MaterialApp(
              title: 'Flutter Demo',
              debugShowCheckedModeBanner: false,
              theme: ThemeData(
                colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
                useMaterial3: true,
              ),
              home: Builder(builder: (context) {
                return const MainPage();
              }),
            ));
      },
    );
  }
}

class MainPage extends StatefulWidget {
  const MainPage({super.key});

  [@override](/user/override)
  State<MainPage> createState() => _MainPageState();
}

class ItemAction {
  String title;
  Widget page;
  String des;

  ItemAction(this.title, this.des, this.page);
}

class _MainPageState extends State<MainPage> {
  var items = [
    ItemAction("基础网络请求", "基于Dio封装的基础网络请求。", const BaseHttpPage(title: "网络请求")),
    ItemAction(
        "计数器(LiveData)", "最简单使用方式", const CounterPage(title: "LiveData")),
    ItemAction("计数器(LiveData ViewModel)", "观察多个状态变化,主动最小单位刷新",
        const LiveViewModelPage(title: "LiveViewModel")),
    ItemAction("Model-View-Intent", "使用ViewModel实现MVI架构及对组件生命周期感知的网络请求。",
        const HttpViewModelPage(title: "Model-View-Intent")),
    ItemAction(
        "TimeOverlayTier",
        "For Toast & Loading",
        const HintToastDialogPage(
          title: "TimeOverlayTier",
        )),
    ItemAction(
        "EventBus",
        "event bus",
        EventBusPage(
          title: "EventBus",
        )),
    ItemAction(
        "InvokeController",
        "跨组件调用、状态提升",
        const StateControllerPage(
          title: "Invoke Controller",
        )),
    ItemAction(
        "组件链式调用",
        "减少嵌套",
        const WidgetChainPage(
          title: "Widget Chain",
        )),
  ];

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      // backgroundColor: Colors.blue,
      appBar: AppBar(
        // backgroundColor: Colors.blue,
        title: const Text("FLUTTER_BOOT"),
      ),
      body: ListView.separated(
          itemCount: items.length,
          separatorBuilder: (context, index) {
            return const Divider(
              height: 1,
            );
          },
          itemBuilder: (context, index) {
            return InkWell(
              onTap: () async {
                Navigator.push(context, MaterialPageRoute(builder: (context) {
                  return items[index].page;
                }));
              },
              child: Container(
                padding:
                    const EdgeInsets.symmetric(vertical: 8, horizontal: 20),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Row(
                      children: [
                        Text(
                          items[index].title,
                          style: const TextStyle(
                              fontSize: 16, color: Colors.black),
                        ),
                        const Spacer(),
                        const Icon(Icons.chevron_right)
                      ],
                    ),
                    Padding(
                      padding: const EdgeInsets.only(top: 8),
                      child: Text(items[index].des,
                          style: const TextStyle(
                              fontSize: 14, color: Colors.black54)),
                    )
                  ],
                ),
              ),
            );
          }),
    );
  }
}

更多关于Flutter启动配置插件flutter_boot的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter启动配置插件flutter_boot的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter项目中,flutter_boot 并不是一个广泛认知或官方维护的插件。通常,Flutter应用的启动配置是通过修改 main.dart 文件和 AndroidManifest.xml(针对Android)以及 Info.plist(针对iOS)来实现的。不过,你可能是在寻找一个用于管理启动屏幕、初始化配置等功能的插件。在这种情况下,虽然没有名为 flutter_boot 的标准插件,但我们可以使用类似的插件,比如 flutter_native_splash 来实现启动屏幕的配置。

以下是如何使用 flutter_native_splash 插件来配置Flutter应用的启动屏幕的示例代码:

  1. 添加依赖: 首先,在你的 pubspec.yaml 文件中添加 flutter_native_splash 依赖。

    dependencies:
      flutter:
        sdk: flutter
      flutter_native_splash: ^1.3.2  # 请检查最新版本号
    
  2. 运行配置命令: 在终端中运行以下命令来生成启动屏幕的配置文件。

    flutter pub run flutter_native_splash:create
    

    这将基于你的 pubspec.yaml 中的配置自动生成必要的 Android 和 iOS 启动屏幕文件。

  3. 配置启动屏幕: 在 pubspec.yaml 中配置启动屏幕的颜色、图片等属性。例如:

    flutter_native_splash:
      color: "#ffffff"  # 启动屏幕的背景颜色
      image: assets/splash.png  # 启动屏幕的图片路径
      android: true  # 是否为Android生成启动屏幕
      ios: true      # 是否为iOS生成启动屏幕
      web: false     # 是否为Web生成启动屏幕(如果需要)
    

    确保你的 assets/splash.png 图片已经存在于项目的 assets 文件夹中,并且在 pubspec.yamlflutter 部分正确声明了资源:

    flutter:
      assets:
        - assets/splash.png
    
  4. 清理和重建项目: 运行以下命令来清理并重建你的Flutter项目,以确保新的启动屏幕配置生效。

    flutter clean
    flutter pub get
    flutter run
    
  5. (可选)自定义更多配置flutter_native_splash 插件支持多种自定义选项,如不同的分辨率图片、启动动画等。你可以查阅其官方文档了解更多详细信息。

请注意,虽然上述示例使用了 flutter_native_splash 插件,但如果你确实在寻找一个名为 flutter_boot 的插件,并且它存在于某个特定的库或私有仓库中,那么你可能需要查阅该插件的特定文档或联系其维护者以获取正确的使用方法和代码示例。在大多数情况下,启动配置的需求可以通过类似 flutter_native_splash 的插件来满足。

回到顶部