美文网首页Flutter
Flutter 入门使用

Flutter 入门使用

作者: GoGoCoding | 来源:发表于2019-10-17 10:15 被阅读0次

    Flutter

    概要

    Flutter是一个由谷歌开发的开源移动应用软件开发工具包。它是以dart为基础做出的一套SDK,支持在Android和iOS上构建APP。简单来说它与RN、Weex等众多混合开发平台框架一样,都是一套代码实现多平台发布的跨平台框架。

    Flutter与其他跨平台的最大不同之处是它自建了一个2D渲染引擎.

    • Flutter 框架
    flutter框架
     * Flutter框架可分为Framework层和Engine层;
    
        - Flutter Framework: 整个框架层都是用Dart语言实现,该层提供一套基础库, 用于处理动画、绘图和手势等。并且基于绘图封装了一套 UI组件库,并且细分为两种风格的组件.
        
            1. Materail : Android风格的Widget;
            2. Cupertino: IOS风格的Widget;
    
        - Flutter Engine: 这是一个纯 C++实现的框架层,包含了 Skia引擎(高性能渲染引擎)、Dart运行环境、文字排版引擎等。
    

    1. 配置Flutter环境

    参考:

    flutter 中文网

    flutter English

     flutter doctor
    

    1.1 初始化项目

    • 纯flutter工程d
        flutter create myapp 
        cd myapp
        flutter devices
        flutter run -d <deviceID>
    
    • flutter module
       flutter create -t module smarthome_flutter
    
    • 默认情况下,模板支持使用Java编写Android代码、用Objective-C编写iOS代码;要使用Kotlin或Swift,请使用-i和/或-a标志:
    flutter create -i swift -a kotlin myapp
    

    SmartHome集成flutter 目前采用的是flutter module的模式

    1.2 Flutter 版本

    #查看当前使用的分支
    flutter channel 
    
    #切换分支 stable为flutter的稳定版分支
    flutter channel stable
    flutter channel master
    flutter channel beta
    flutter channel dev
    

    1.3 升级 Flutter channel 和 packages

    • 要同时更新Flutter SDK和你的依赖包,在你的应用程序根目录(包含pubspec.yaml文件的目录)中运行flutter upgrade 命令
    flutter upgrade
    
    • 如果您修改了pubspec.yaml文件,或者只想更新应用依赖的包(不包括Flutter SDK),请使用以下命令:
    #获取pubspec.yaml文件中列出的所有依赖包
    flutter packages get
    #获取pubspec.yaml文件中列出的所有依赖包的最新版本
    flutter packages upgrade
    

    2. 如何集成Flutter

    2.1 确保所有的submodule已下载

    Native工程下 feature-flutter分支 执行 git submodule update

    2.2 生成flutter module的个人本地配置

    cd [Your SmartHome Project Path]/smarthome_flutter

    flutter run

    2.3 cocoapods 集成flutter

    回到Native工程的路径下 pod install

    2.4 Q&A

    • 集成flutter后,xcode 编译运行APP时 ,报:
      error: Multiple commands produce .......
    *   打开.ios/Runner.xcworkspace -> TARGETS -> Runner      -> Build Phases -> Embed Frameworks  然后删除       flutter.frameworks   
    
    • flutter 中webView打不开,log: Trying to embed a platform view but the PaintContext does not support embedding
      • 在.iOS/ 目录下Runner.xcworkspace工程中 的info.plist 文件中添加配置
      • key = io.flutter.embedded_views_preview
      • value = YES
    • Lost connection to device

      • brew upgrade --fetch-HEAD usbmuxd

    3. 原生和Flutter相互通信 platform channels

    通过platform channels 在flutter 和宿主(ios/andriod)之间传递消息,如下图所示:

    platform channels

    Samples

    • flutter端 关键代码
    
    
    import 'package:flutter/services.dart';
    import 'package:json_annotation/json_annotation.dart';
    import 'package:smarthome_flutter/common/tools/SHEventBus.dart';
    part 'platfrom.g.dart';
    
    var sh_event_bus = EventBus();
    class SHPlatform{
      static final MethodChannel channel =  _setupChannel();
    
    
    
      static MethodChannel _setupChannel(){
        var channel = MethodChannel("com.jd.smarthome",StandardMethodCodec());
        
        channel.setMethodCallHandler((MethodCall call) async {
          String method = call.method;
          dynamic arguments = call.arguments;
          switch(method){
            case 'updateInfo':
              sh_event_bus.emit('updateHomePage');
            break;
    
            default:
              throw new PlatformException(code:"");
          }
        });
    
        return channel;
      }
    
      static DeviceInfo _deviceInfo;
      static Future<DeviceInfo> getDeviceInfo() async{
        if(_deviceInfo == null){
          return SHPlatform.channel.invokeMethod("deviceInfo")
          .then((onValue){
            _deviceInfo = DeviceInfo.fromJson(Map<String, dynamic>.from(onValue));
            return _deviceInfo;
          });
        }else{
          return Future.value(_deviceInfo);
        }
      }
    }
    
    @JsonSerializable()
    class DeviceInfo{
      String platform;
      String hardPlatform;
      String systemVersion;
      String appVersion;
      String channel;
      String deviceId;
      String tgt;
      String pin;
      DeviceInfo();
      factory DeviceInfo.fromJson(Map<String, dynamic> json) => _$DeviceInfoFromJson(json);
    }
    
    
    • iOS 端关键代码
    -(void)showFlutterView{
        FlutterEngine *flutterEngine = [(AppDelegate *)[[UIApplication sharedApplication] delegate] flutterEngine];
        
        __weak __typeof(self) weakSelf = self;
        
        // 要与main.dart中一致
        NSString *channelName = @"com.jd.smarthome";
        
        //FlutterMethodChannel *messageChannel;
        self.messageChannel = [FlutterMethodChannel methodChannelWithName:channelName
                                                                           binaryMessenger:flutterEngine];
        [self.messageChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
            
            if ([call.method isEqualToString:@"toNativeSomething"]) {
                UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"flutter回调" message:[NSString stringWithFormat:@"%@",call.arguments] delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil];
                [alertView show];
                
                // 回调给flutter
                if (result) {
                    result(@10);
                }
            } else if ([call.method isEqualToString:@"toNativePush"]) {
                
                //[GLOBAL_Nav pushViewController:vc animated:YES andLastName:getClassName];
                
            } else if ([call.method isEqualToString:@"toNativePop"]) {
                [GLOBAL_Nav popViewControllerAnimated:YES];
            }else if([call.method isEqualToString:@"deviceInfo"]){
                NSDictionary* data = @{
                                       @"platform":kPlatValue,
                                       @"hardPlatform":[CommonMethod getPlatformName],
                                       @"systemVersion":[CommonMethod getSystemVersion],
                                       @"appVersion":[CommonMethod getAppVersion],
                                       @"channel":kChannelValue,
                                       @"deviceId":[OpenUDID value],
                                       @"tgt":[LoginObjectClass sharedLoginObjectClass].A2String,
                                       @"pin":[LoginObjectClass sharedLoginObjectClass].pin,
                                       };
                result(data);
            }else{
                result(FlutterMethodNotImplemented);
            }
        }];
        
        FlutterViewController *flutterViewController = [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];
        flutterViewController.navigationItem.title = @"Flutter Demo";
        
        [GLOBAL_Nav pushViewController:flutterViewController animated:YES andLastName:getClassName];
        
        [self updateFlutterHomePage];
    }
    
    -(void)updateFlutterHomePage{
        
        [self.messageChannel invokeMethod:@"updateInfo" arguments:@{@"page": @"1"} result:^(id  _Nullable result) {
            
            if([result isKindOfClass:[FlutterError class]]){
        
                FlutterError *error = (FlutterError *)result;
                SHLogError(kLogFlutter, @"updateInfo with error message: %@", error.message);
                
            }else  if (result == FlutterMethodNotImplemented) {
                SHLogError(kLogFlutter, @"updateInfo was unexepectedly not implemented ");
                
            }else{
                SHLogInfo(kLogFlutter, @"updateInfo success");
                
            }
            
        }];
    }
    

    4 Flutter 状态管理 ----Provider

    4.1 为什么需要状态管理

    state1

    在 State 属于某一个特定的 Widget,在多个 Widget 之间进行交流的时候,虽然你可以使用 callback 解决,但是当嵌套足够深的话,我们增加非常多可怕的垃圾代码。

    随着功能的增加,你的应用程序将会有几十个甚至上百个状态。这个时候应用应该会像下图一样。

    这时候,我们便迫切的需要一个架构来帮助我们理清这些关系,状态管理框架应运而生。

    state2

    4.2 怎么用provider

    4.2.1、添加依赖

    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: ^0.1.2
      webview_flutter: ^0.3.9+1
      dio: 2.1.10
      json_annotation: ^2.0.0
      provider: ^3.0.0
      intl: ^0.15.7
      crypto: ^2.0.6
      logging:
      cached_network_image: ^1.1.1
      flutter_easyrefresh: ^1.2.7
    

    4.2.2、创建数据Model

    //这里使用了 mixin 混入了 ChangeNotifier,这个类能够帮助我们自动管理所有listeners。当调用 notifyListeners() 时,它会通知所有听众进行刷新。
    
    class CounterModel with ChangeNotifier {
      int _count = 0;
      int get value => _count;
    
      void increment() {
        _count++;
        notifyListeners();
      }
    }
    

    4.2.3、创建顶层共享数据

    class GlobalConfig{
      static get version => '0.0.3';
      static get counter => CounterModel();
    
      static ThemeData get themeData => ThemeData(
        primaryColor: Color(0xff181B34),
        textTheme: TextTheme(
          title: TextStyle(
            fontSize: 14,
            color: Colors.white
          )
        )
      );
    
      static List<SingleChildCloneableWidget> get providers => [
        Provider<String>.value(value:GlobalConfig.version),
        ChangeNotifierProvider<CounterModel>.value(
          value: GlobalConfig.counter,
        )
      ];
    }
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MultiProvider(
          providers: GlobalConfig.providers,
          child: MaterialApp(
            title: 'SmartHome demo',
            theme: GlobalConfig.themeData,
            home: MyHomePage(
              title: "APP",
            ),
            debugShowCheckedModeBanner: false,
          ),
        );
      }
    }
    

    4.2.4、在子页面中获取状态

    • Provider.of(context)
    class FirstScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final _counter = Provider.of<CounterModel>(context);
    
        return Scaffold(
          appBar: AppBar(
            title: Text('FirstPage'),
          ),
          body: Center(
            child: Text(
              'Value: ${_counter.value}',
              style: TextStyle(fontSize: 11),
            ),
          ),    
        );
      }
    }
    
    
    • Consumer
    class SecondPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
      
      
        return Scaffold(
          appBar: AppBar(
            title: Text('Second Page'),
          ),
          body: Consumer2<CounterModel,string>(
            builder: (context, CounterModel counter, string version, _) => Center(
                  child: Text(
                    'Value: ${counter.value}' + version,
                    style: TextStyle(fontSize: 11,
                   ),
                ),
             ),
          ),
        );
      }
    }
    
    • Different ?
      • 通过阅读源码可以发现,Consumer 就是通过 Provider.of<T>(context) 来实现的。但是从实现来讲 Provider.of<T>(context) 比 Consumer 简单好用,但是我们推荐优先使用Consumer,好处在于能够在复杂项目中,极大地缩小你的控件刷新范围。Provider.of<T>(context) 将会把调用了该方法的 context 作为听众,并在 notifyListeners 的时候通知其刷新。

    5 Map -> model

    介绍一下官方推荐的json_serializable package包。 它是一个自动化的源代码生成器,可以在开发阶段为我们生成JSON序列化模板.

    5.1、在项目中设置json_serializable

    dependencies:
      # Your other regular dependencies here
      json_annotation: ^2.0.0
    
    dev_dependencies:
      # Your other dev_dependencies here
      build_runner: ^1.0.0
      json_serializable: ^3.0.0
    
    

    5.2 以json_serializable的方式创建model类

    import 'package:json_annotation/json_annotation.dart';
    part 'skill_model.g.dart';
    
    @JsonSerializable()
    class SkillList {
      SkillList();
      int page_size;
      int sum_size;
      int current_page;
    
      List<SkillListItem> skills;
      factory SkillList.fromJson(Map<String, dynamic> json) => _$SkillListFromJson(json);
      Map<String, dynamic> toJson() => _$SkillListToJson(this);  
    }
    //一个SkillList.fromJson 构造函数, 用于从一个map构造出一个 User实例 map structure
    //一个toJson 方法, 将 User 实例转化为一个map.
    
    
    • json_serializable第一次创建类时,您会看到与下图类似的错误。
    jsonError
    #一次性生成
    flutter packages pub run build_runner build
    
    #持续生成
    flutter packages pub run build_runner watch
    
    • 使用
    
    request.parseFunction = (json){
          SkillList tmp = SkillList.fromJson(json);
          Map<String, dynamic> newMap = tmp.toJson();
          return SkillList.fromJson(json);
        };
    

    6 异步async、await和Future

    Flutter中,虽然Dart是基于单线程模型的,但是这并不意味着我们没法完成异步操作。在Dart中我们可以通过async关键字来声明一个异步方法,在异步方法中可以使用await表达式挂起该异步方法中的某些步骤,从而实现等待某步骤完成的目的; Future是Dart中提供的一个类,它用于封装一段在将来会被执行的代码逻辑。

    Note

    • async修饰的异步方法需要声明返回一个Future类型,如果方法体内没有主动的返回一个Future类型,系统会将返回值包含到一个Future中返回。

    • await表达式的表达式部分需要返回一个Future对象。

    • await表达式需要在一个async修饰的方法中使用才会生效。 关于async和await的更多详情可以参阅官方文档

    end

    推荐

    flutter 中文

    flutter English

    flutter github

    Dart Packages

    An open list of apps built with Flutter

    完整项目 Flutter / RN / Kotlin / Weex

    相关文章

      网友评论

        本文标题:Flutter 入门使用

        本文链接:https://www.haomeiwen.com/subject/yxkmmctx.html