AI编程,浅尝Flutter

96 阅读5分钟

前段时间第一次尝试写Flutter项目,公司的一个小APP,不过麻雀虽小,五脏俱全,接下来就简单讲讲整个完成过程、代码架构、技术选型吧,这里做个记录,方便以后回顾。

AI强势开局

这个项目需要适配Android与iOS端,且指定Flutter开发。我是Android开发者,第一次接触Flutter,没有学习,直接拿AI完成环境配置、编码。我用的是TRAE,买了Pro包月,性价比挺高的。TRAE的IDE可以直接打开项目目录,它会以项目为上下文,可以更加精准地理解我们的命令。

首先编辑器用 Android Studio,因为更加熟悉。开局先让AI自己创建一个Flutter项目,并自行安装好Flutter环境,然后给AS安装Flutter和Dart的插件。可以指定下Flutter的安装目录,不要装在项目里,当然装错也没事,让AI改就行。疑难杂症都抛给AI,真的效率太高了。

因为要跑iOS,所以用的是mac电脑,运行iOS应用需要安装xCode,安装xCode之前需要升级系统到最新版,安装完xCode后再下载最新的iOS模拟器即可,然后就可以从AS里面直接启动模拟器了。

image.png

运行App的话需要在xCode配置BundleId,随意写个喜欢的ID,苹果不像安卓一样要手动写到项目内,注意不用付年费就可运行App,但是如果要实现苹果推送就要充个开发者会员了。

项目架构

目录结构

作为新手,先看下Flutter项目的目录结构:

image.png
  • android - 包含 Android 特定的文件
  • ios - 包含 iOS 特定的文件
  • assets - 静态资源,如图片、字体、JSON文件等
  • lib - Dart代码,flutter核心代码,main.dart 为程序运行入口文件
  • test - 测试文件
  • pubspec.yaml - 配置文件,管理第三方依赖、图片、字体等资源
  • gromore_ads - 本地引入的一个flutter插件

核心库

下面是一些常用库,主要也是参考了GitHub中的优秀Demo项目 github.com/simplezhli/…

gromore_ads:
  path: ./gromore_ads
# Toast插件 https://github.com/OpenFlutter/flutter_oktoast
oktoast: ^3.4.0
# 网络库 https://github.com/cfug/dio
dio: ^5.9.0
# Dart 常用工具类库 https://github.com/Sky24n/common_utils
common_utils: 2.1.0
# WebView插件 https://github.com/flutter/packages/tree/main/packages/webview_flutter
webview_flutter: 4.13.0
# 快捷存储 https://juejin.cn/post/6844903873262387207
sp_util: 2.0.3
# 格式化String https://github.com/Naddiseo/dart-sprintf
sprintf: ^7.0.0
# 获取当前设备信息 https://github.com/fluttercommunity/plus_plugins/tree/main/packages/device_info_plus
device_info_plus: 12.2.0
# 路由框架 https://github.com/theyakka/fluro
fluro: ^2.0.5
# 图片缓存 https://github.com/renefloor/flutter_cached_network_image
cached_network_image: ^3.4.0
# 下拉刷新
easy_refresh: ^3.4.0
# 网络状态 https://github.com/fluttercommunity/plus_plugins/tree/main/packages/connectivity_plus
connectivity_plus: ^6.0.5

Flutter的Dio和Android的OkHttp很像,功能强大支持自定义拦截器,用起来也是很丝滑的。还有Flutter各种跳转用路由框架fluro也很方便,也有统一的生命周期方法(didPush、didPop等)。这里建议先了解下程序入口「main.dart」的相关代码,学习下「MaterialApp」的相关参数。把APP的启动流程捋清楚,可以事半功倍。

页面架构

关于页面代码的完成,都是直接将UI出的界面丢给AI,AI会完成一个基本的、丑丑的界面,然后自己一点点优化,当然,也可以不断输入命令让AI一直改。网络请求、架构分层这些,都可以让AI写,把核心信息给AI即可。下面看下AI写的代码。

架构选型时,页面架构在getX与RiverPod中选择了后者,RiverPod遵循响应式编程,整个代码写下来非常舒服,和Compose的写法很像了。举个例子,收藏页面,首先也是把界面信息封装成一个「state」:

image.png

这个页面里是一个列表,支持下拉刷新、上拉加载更多、编辑模式,编辑模式下可以多选,相关变量都集中在了「CollectionState」中了,「CollectionsNotifier」会持有state对象,然后提供一个provider供界面监听,在界面中使用「select」进行精细化监听:(CollectionScreen就是收藏界面)

image.png

当界面初始化时触发「initState」,这里调用「CollectionsNotifier」的fetchInitial获取数据,获取后修改state就会自动触发界面变化了:

image.png

这里网络/数据库/mock数据请求都单独写在了另一层代码中,总共三层代码,整体代码就很方便阅读,fetchInitial会调用到queryCollections:

image.png

Flutter里面也是可以写页面的基础类的,不过这里是用「with」继承:

image.png

这个基础类我封装了Progress相关方法:

mixin BasePage<T extends ConsumerStatefulWidget> on ConsumerState<T> {
  bool _isShowDialog = false;

  void closeProgress() {
    if (mounted && _isShowDialog) {
      _isShowDialog = false;
      NavigatorUtils.goBack(context);
    }
  }

  void showProgress() {
    /// 避免重复弹出
    if (mounted && !_isShowDialog) {
      _isShowDialog = true;
      try {
        showDialog<void>(
          context: context,
          barrierDismissible: false,
          barrierColor: const Color(0x00FFFFFF),
          // 默认dialog背景色为半透明黑色,这里修改为透明(1.20添加属性)
          builder: (_) {
            return WillPopScope(
              onWillPop: () async {
                // 拦截到返回键,证明dialog被手动关闭
                _isShowDialog = false;
                return Future.value(true);
              },
              child: buildProgress(),
            );
          },
        );
      } catch (e) {
        /// 异常原因主要是页面没有build完成就调用Progress。
        debugPrint(e.toString());
      }
    }
  }

  /// 可自定义Progress
  Widget buildProgress() => const ProgressDialog(hintText: '正在加载...');
}

这里面也可以重写页面生命周期,可以统一实现一些需求。有一点要吐槽下,Flutter的private变量居然是用下划线前缀标记,很特别。

与Android/iOS原生代码互相请求

/// 原生调Flutter
static void setup() {
  final channel = MethodChannel(AppConfig.deviceInfoChannel);
  channel.setMethodCallHandler((MethodCall call) async {
    if (call.method == _methodOnToken) {...}
  }
}

/// Flutter调原生
static Future<void> startCellularRestrictedStateListener() async {
  try {
    final platform = MethodChannel(AppConfig.deviceInfoChannel);
    await platform.invokeMethod(_methodStartCellularListen);
  } on PlatformException catch (e) {
    Log.e('startCellularRestrictedStateListener error: ${e.message}');
  }
}

以上代码都是AI生成,直接输入相关命令,AI会完成很漂亮的代码。

END

最后,AI很强大,可以让我们从0开始,让我们能迅速学会相关知识,但是完成项目也需要更精准的AI命令,需要人与AI的不断磨合,最后才能完成一个精准实现的APP。真正实践的过程中,还是要人下场修改代码的,所以使用AI的过程也是学习的过程,需要最终能读懂AI的代码。还有,使用AI时建议结合git一起使用,能很好的对每次修改进行review。

以上,记录一下,欢迎批评指正,各种交流。