Flutter实战篇

第四章Flutter实战

4.1 Fluter APP 代码结构

1.png

  • ''lib” Dart代码目录
    2.png

  • “ios”、“android”是两个平台相关代码、配置目录

  • pubspec.yaml是依赖的组件库配置如:

environment:
  sdk: ">=2.12.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  http: 0.13.3
  dio: ^4.0.0  #大于等于4.0.0小于5.0.0
  image_picker: ^0.8.3+2

也可以配置本地图片,在工程目录下创建个images文件,将所需图片导入到该目录,并配置如下:

  # To add assets to your application, add an assets section, like this:
  assets:
     - images/

注意: 由于 yaml 文件对缩进严格,所以必须严格按照每一层两个空格的方式进行缩进,此处 assets 前面应有两个空格

4.2 代码实战

模拟微信APP,项目名称WeChatDemo。

  • 项目入口:main
void main() {
  runApp(MyApp());

}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        highlightColor: Color.fromRGBO(1, 0, 0, 0.0),
        splashColor: Color.fromRGBO(1, 0, 0, 0.0),
        cardColor: Color.fromRGBO(1, 1, 1, 0.65),//有透明层叠视图的设置
        primarySwatch: Colors.blue,
        appBarTheme: AppBarTheme(iconTheme: IconThemeData(color: Colors.black))
      ),
      home: RootPage(),
    );
  }
}
  • 主要模块:微信、通讯录、发现、我
 class RootPage extends StatefulWidget {
  const RootPage({Key? key}) : super(key: key);

  @override
  _RootPageState createState() => _RootPageState();
}

class _RootPageState extends State<RootPage> {
  int _currentIndex = 0;
  List <Widget> _pages = [ChatPage(),FriendsPage(),DiscoverPage(),MinePage()];
  final PageController _controller = PageController(initialPage: 0);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: PageView( //通过pageView 来保持状态
        physics: NeverScrollableScrollPhysics(), //不允许滚动
        children: _pages,
        controller: _controller,
      ),
      bottomNavigationBar: BottomNavigationBar(
        onTap: (index){
          setState(() {
            _currentIndex  = index;
          });
          _controller.jumpToPage(index);
        },
        selectedFontSize: 12,
        unselectedFontSize: 12,
        currentIndex: _currentIndex,
        fixedColor: Colors.green,
        type: BottomNavigationBarType.fixed,
        items: [
          BottomNavigationBarItem(
              icon: Image.asset('images/tabbar_chat.png',height: 25,width: 25),
              activeIcon: Image.asset('images/tabbar_chat_hl.png',height: 25,width: 25,),
              label: '微信'
          ),
          BottomNavigationBarItem(
              icon: Image.asset('images/tabbar_friends.png',height: 25,width: 25,),
              activeIcon: Image.asset('images/tabbar_friends_hl.png',height: 25,width: 25,),
              label: '通讯录'
          ),
          BottomNavigationBarItem(
              icon: Image.asset('images/tabbar_discover.png',height: 25,width: 25,),
              activeIcon: Image.asset('images/tabbar_discover_hl.png',height: 25,width: 25,),
              label: '发现'
          ),
          BottomNavigationBarItem(
              icon: Image.asset('images/tabbar_mine.png',height: 25,width: 25,),
              activeIcon: Image.asset('images/tabbar_mine_hl.png',height: 25,width: 25,),
              label: '我'
          ),
        ],
      ),
    );
  }
}

详细代码请参考项目WechatDemo

4.2.1 AutomaticKeepAliveClientMixin 混入

state 混入 AutomaticKeepAliveClientMixin,保持Tabbar 切换时state不被重新初始化

  • state 混入 AutomaticKeepAliveClientMixin
class _ChatPageState extends State<ChatPage>
    with AutomaticKeepAliveClientMixin
  • state 添加成员属性
  @override
  bool get wantKeepAlive => true;
  • 在 build 方法中添加
    super.build(context);
4.2.2 混合开发
  • Flutter调原生功能,如我的页面,更换头像需要调用系统相册,有两种实现方式
    1、MethodChannel 通道
    类似于通知,通过发送消息,监听消息回调实现与原生通讯
//第一步:Flutter端创建通道,传入唯一标识
 MethodChannel _methodChannel = MethodChannel('mine_page');
 
//第二步:Flutter端发送消息
_methodChannel.invokeListMethod('picture'); //发送了一条要更换图片的消息

//第三步:原生注册‘mine_page’通道,并监听消息发送
FlutterViewController *vc = (FlutterViewController *)self.window.rootViewController;
FlutterMethodChannel *methodChannel = [FlutterMethodChannel methodChannelWithName:@"mine_page" binaryMessenger:vc];
self.methodChannel = methodChannel;
//监听flutter消息
[methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
     if ([call.method isEqualToString:@"picture"]) {
           UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
           imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
           imagePicker.delegate = self;
           imagePicker.modalPresentationStyle = UIModalPresentationFullScreen;
           [vc presentViewController:imagePicker animated:YES completion:nil];
      }
 }];

//第四步:原生发送拿到相册消息
[self.methodChannel invokeMethod:@"imagePath" arguments:imagePath];

//第五步:dart监听回调,并更新状态
 _methodChannel.setMethodCallHandler((call) {
     if(call.method == 'imagePath') {
         String imagePath = call.arguments.toString().substring(7); //截取前面7个字符,拿到的参数带有file://
         setState(() {
           _avatarFile = File(imagePath);
         });
      }
    return Future(() => null);
 });

注:原生与Flutter交互的过程中必须确保是同一个通道,唯一性

2、ImagePicker,三方插件无需原生写多余代码

  void _imagePick() async {
    XFile? file =  await ImagePicker().pickImage(source: ImageSource.gallery); //相册
    if(file != null) {
      setState(() {
        _avatarFile = File(file.path);
      });
    }
  }
  • 原生项目中使用Flutter页面
    1、创建Flutter 组件,在组件项目中写dart代码
    3.png

    • Flutter App: 创建一个完整的flutter项目,里面包含安卓和iOS项目
    • Flutter Module: 创建Flutter组件时使用,混编到已有的安卓/iOS工程
    • Flutter Plugin:就是Plugin的方式进行开发,比如一些百度地图,flutter不提供,通过自建Plugin,通过Plugin中的android和ios原生项目,集成原生百度功能,通过plugin中的lib中的方法来进行flutter调用百度的相关方法
    • Flutter Package:纯Dart插件工程,仅包含Dart层的实现,往往定义一些公共Widget

    2、通过cocoapods 导入到原生项目中,如:本地有个flutter_moudle组件,原生项目中需要植入Flutter页面,podfile文件配置如下:
    4.png

    3、导入Flutter库,并使用
    注意:flutter页面和原生页面频繁切换会导致内存泄漏

4.2.3 安装包结构

用Xcode打开项目,选择真机编译,选择Products ->Runner.app -> 右键Show in Finder右键显示包内容,可以看到编译后结构:可执行文件、资源文件、签名、Frameworks。重点关注下Frameworks
5.png

  • App.framewok: dart代码编译后的产物,App可执行文件(二进制机器代码)
  • Flutter.framewok: 就是Flutter SDK源码文件,我们看到可执行文件大小是65.7M,而Flutter SDK目录下的同名framework也是65.7M,很好的证明了APP在打包过程中会将Flutter SDK 引擎导入到安装包中,这也是Flutter App安装包比原生项目体积大的原因
    6.png

路径:/flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework/ios-armv7_arm64/Flutter.framework

4.3 Flutter 插件开发(plugin )
  • 1、Flutter插件介绍
    一种专用的Dart包,其中包含用Dart代码编写的API,以及针对Android(使用Java或Kotlin)和针对iOS(使用OC或Swift)平台的特定实现(另外也可以包含Native的组件代码),也就是说插件包括原生代码与Dart代码。插件开发完成后,将上传到dart插件管理服务仓库,类似于maven、pod库,然后在flutter开发过程中可以通过pubspec.yaml(dart包管理配置文件)来获取插件服务。

  • 2、为什么要开发Flutter插件
    首先,虽然Flutter的生态现在已经越来越完善了,但是相比于Android跟iOS原生的生态体系,还是远远不够。很多在Android跟iOS原生上有的很酷炫的库,在Flutter中还没有或者是并没有那么的完善。其次,想必大家在原生工程里都有一套用了多年的稳定基础组件,包括网络组件、数据组件等,要重新在Flutter中用dart来搭建一套,时间成本、风险成本、组件兼容性等都是不可控的。所以,最理想的方式就是Flutter的基础组件可以对我们现有原生的组件做一层包装,然后提供接口给Flutter模块进行调用,这样一来什么时间、风险、兼容性都不是问题。我们只要维护一套原生组件就好,Flutter组件只是一层包装,并不在意内部如何去实现。那么Flutter跟原生怎么进行交互呢?

  • 3、Flutter如何与原生交互
    1)MethodChannel 通道, 具体使用4.2.2混合开发已介绍。Flutter与原生的交互模型,类似于一种C-S模型。其中Flutter为Client层,原生为Server层,两者通过MethodChannel进行消息通信,原生端向Flutter提供已有的Native组件功能。

    2)究竟什么是MethodChannel
    Flutter定义了3种Channel模型,分别是: BasicMessageChannel:用于传递字符串和半结构化的信息;MethodChannel:用于传递方法调用(methodinvocation)EventChannel: 用于数据流(event streams)的通信。3种channel基本类似,这里以MethodChannel展开介绍
    3) MethodChannel有3个重要的成员变量:

- String name    
一个Channel对象通过name来进行唯一的标识,所以在Channel的命名上一定要独一无二,推荐采用组件名_Channel名 组合来进行命名

- BinaryMessenger messenger   
BinaryMessenger是Platform端与Flutter端通信的工具,其实是个中间信使,当我们初始化一个Channel,并向该Channel注册处理消息的Handler时,实际上会生成一个与之对应的BinaryMessageHandler,并以channel name为key,注册到BinaryMessenger中。当Flutter端发送消息到BinaryMessenger时,BinaryMessenger会根据其入参channel找到对应的BinaryMessageHandler,并交由其处理。BinaryMessenger维护了一个map

Binarymessenger在Android端是一个接口,其具体实现为FlutterNativeView。而其在iOS端是一个协议,名称为FlutterBinaryMessenger,FlutterViewController遵循了它。Binarymessenger只和BinaryMessageHandler打交道,而Channel和BinaryMessageHandler则是一一对应的。由于Channel从BinaryMessageHandler接收到的消息是二进制格式数据,无法直接使用,故Channel会将该二进制消息通过Codec(消息编解码器)解码为能识别的消息并传递给Handler进行处理。当Handler处理完消息之后,会通过回调函数返回result,并将result通过编解码器编码为二进制格式数据,通过BinaryMessenger返回。

- MethodCodec codec  
消息编解码器Codec主要用于将二进制格式的数据转化为Handler能够识别的数据,MethodCodec主要是对MethodCall中这个对象进行序列化与反序列化,MethodCall是Flutter向Native发起调用产生的对象,其中包含了方法名以及一个参数集合

  • 4、插件工程创建
    推荐通过命令行来创建,因为通过IDE来创建有时候会卡住,而且会比较慢
flutter create --org com.plug.jcfc.cn --template=plugin --platforms=android,ios -i objc -a java flutter_plug

  • 5、目录结构
    [图片上传失败…(image-236f71-1635245763669)]

  • ib dart模块

  • android android模块

  • ios iOS模块

  • example 示例测试工程可用于插件的调试

  • pubspec.yaml flutter项目的配置文件
    ….

    pubspec.yaml: dart生态下的包管理配置文件类似 Android中的gradle、iOS中的Podfile,在这里可以统一管理整个flutter工程的dart依赖包,以及管理整个插件的发布属性。

  • 6、原生端开发
    实现MethodCallHandler接口,注册MethodChannel对象,MethodChannel在创建时一定要保证name唯一
    将MethodHandler接口注册到MethodChannel中
    包装原生端组件,包括一些二方库、三方库,将包好的方法通过MethodCallHandler暴露给Flutter端

  • 7、Flutter端开发
    找到MethodChannel对象,通过唯一标识name,注意(name一定要与原生端注册的一致)
    定义dart方法,因为要保证方法的执行不产生阻塞,所以推荐用Future async await .相关的语法见dart语法
    调用methodChannel.invokeMothed()与原生进行通信

  • 8、插件测试
    在example/lib/main.dart下调用插件中的方法,然后直接通过命令将工程跑起来查看输出

fultter run

插件都还没有发布,为什么example工程可以直接引用?看一下example目录下的pubspec.yaml文件,里面有

dependencies:
  flutter:
    sdk: flutter

  flutter_plug:
    # When depending on this package from a real application you should use:
    #   flutter_plug: ^x.y.z
    # See https://dart.dev/tools/pub/dependencies#version-constraints
    # The example app is bundled with the plugin so we use a path dependency on
    # the parent directory to use the current plugin's version.
    path: ../ 相对路径

pubspec.yaml 不但可以引用服务器上的插件,也可以引用本地路径下的插件。如此我们可以在插件未发布的情况下,直接在本地的测试工程里对插件进行测试

  • 9、插件发布
  • 完善pubspec.yaml文件
name: 插件名称  
description: 插件描述  
version: 0.0.1 版本号  
homepage: 项目主页地址  
publish_to: 填写私有服务器的地址(如果是发布到flutter pub则不用填写,插件默认是上传到flutter pub)  
  • 检查发布条件
flutter packages pub publish --dry-run  

--dry-run 参数表示本次执行会检查插件的配置信息是否有效,插件是否满足上传条件。如果成功的话并不会真正的将插件上传,而是会显示本次要发布插件的信息,并提示成功。一般在插件的正式发布前,建议先执行该命令,避免在上传过程中出现错误
  • 正式发布
发布至pub平台
flutter packages pub publish  

4.3 Flutter 插件开发(Package)

纯Dart 语言,封装的有特定功能的Widget组件,创建和发布流程和plugin插件类似


参考资料
Flutter中文网
Dart和Flutter应用程序的官方软件包库
Dart语言中文网

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

迷茫的码农小学生

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值