美文网首页rtzl
【Flutter 手机 app 开发】

【Flutter 手机 app 开发】

作者: zbcy0012 | 来源:发表于2020-07-26 10:15 被阅读0次

    【前言】

    就目前而言,由于之前从未接触过iOS开发工作,目前尚处于摸索过程中,一切工作成果以初步实现为最基准。
    本文记载我研究如何使用 flutter 的过程,以 iOS 端为基础端,以及遇到的一些问题。

    ==================

    一、本机调试运行

    2020-12-28 起改为 flutter 框架。
    现已成功上传至 testFlight

    【研发行动步骤】

    • 找一个适合的 coding 编辑器
      vs code for mac

    • 真机调试
      (need registered device)
      flutter run

    • 了解代码结构、代码拆分

    • 创建一般页面

    • 添加页面交互
      就是特定可交互组件的特定属性值的设定

    • 路由实现:
      还是静态路由香

    1.一般前进导向

     Navigator.of(context).push(new MaterialPageRoute(builder:(context){...}))
    

    2.带参数前进导向
    就像是props一样直接往方法的括号里面放,但是要注意强类型语言的参数定义与传递跟js略有不同。
    方式一:

    // 传参
    Navigator.of(context)
            .pushNamed('/navigation2', arguments: {"start": start, "end": end});
    // 接收参数
    var obj = ModalRoute.of(context).settings.arguments;
    

    3. 后退
    由push构成的一般前进动作,appBar 部分会自带后退按钮,无需自定义
    4.带参数后退(或者带参数伪后退)
    但是只能带一个参数回来
    5.不可逆前进(如登陆后、若不注销账户则不再能退到登陆页)

    //从登录到主页
    Navigator.of(context).pushReplacementNamed('/home');
    
    //一般前进
    Navigator.of(context).pushNamed("/page1");
    
    // 退出登录
    while (Navigator.of(context).canPop()) {
      Navigator.of(context).pop();
    }
    Navigator.of(context).pushReplacementNamed("/login");
    

    6.底部菜单式目录
    直接在 scaffold 的 bottomNavigationBar 接口处使用 BottomNavigationBar 组件


    • 组件拆分:props传递与state管理
    class TopBar extends StatelessWidget with PreferredSizeWidget {
    
      /**********************关键代码**********************/
      final title;
      TopBar({@required this.title});
      /**********************关键代码**********************/
    
      @override
      Widget build(BuildContext context) {
        return AppBar(title: Text('${this.title}'));
      }
    
      @override
      Size get preferredSize => Size.fromHeight(kToolbarHeight);
    }
    

    外部调用

    TopBar(title: 'HomePage Title'),
    
    • 基本样式掌控
      ✓1.沉浸式状态栏
      好像 ios app 默认都是沉浸式无需特殊设置,而网上大部分沉浸式状态栏都是在针对安卓 app。
      ✓2.自定义顶部条高度、颜色、字体;后退按键颜色
      ✓3.自定义底部条高度、颜色、字体

    • 创建外接式html
      简单粗暴,没有问题。
      h5 与 flutter 的交互实现:
      h5 => flutter:
      理论:在 flutter 端注册 javascriptChannels ,然后监听特定 name 的回传再进行后续动作。
      flutter 端:

      // 创建 JavascriptChannel
    JavascriptChannel _toastJsChannel(BuildContext context) => JavascriptChannel(
          name: 'show_flutter_toast',
          onMessageReceived: (JavascriptMessage message) {
            print("get message from JS, message is: ${message.message}");
           // _webViewController.evaluateJavascript('zbchell("rtzl hasaki")');
          });
    
      @override
      Widget build(BuildContext context) {
        return WebView(
          ...,
          javascriptChannels: <JavascriptChannel>[
            _toastJsChannel(context),
          ].toSet(),
        );
      }
    

    JS 端:

    if(window.show_flutter_toast)
      show_flutter_toast.postMessage(`someString`)
    

    flutter => h5
    理论:flutter 通过 webviewcontroller 的 evaluateJavascript 方法调起 web 端 window 名下的方法。
    JS端:

    // 注册可以被调起的函数
    window.zbchell = function (text) {
      ...,
      // setState
      this.setState({
        ...  // if u need
      })
    }
    

    注意不要使用箭头函数声明,以免之后绑定 this 失败,除非你的这个方法当中并不需要使用特殊指向的 this。

    flutter 端:

      // 创建 JavascriptChannel
      JavascriptChannel _toastJsChannel(BuildContext context) => JavascriptChannel(
          name: 'show_flutter_toast',
          onMessageReceived: (JavascriptMessage message) {
            print("get message from JS, message is: ${message.message}");
            // 执行!!
            _webViewController.evaluateJavascript('zbchell("rtzl hasaki")');
          });
    
    • 创建内嵌式html文件与互相通信、内嵌html且外接css
      1.本地html文件读取运行
      2.本地html与 flutter 互相通信
      暂且免谈

    症状:只能渲染本地的 html 文件,而无法加载 html 对 css 和 js 文件的引入
    解决思路:
    a.整个网页全部外接(部署在服务器上)
    结论:简单粗暴,没有问题。
    b.将所有文件打包至一个 .html 文件当中(付诸实践...)
    结论:大图片等外部资源只要有引入,则一概无法衔接(暂时废弃)


    至此,套壳应用已理论上可实现。


    接下来的内容为边做边研究
    目的:开发一款包含截至目前所有组件的 rtzl 的开发者 app。

    〇、ALL OUT

    1.修改 app 桌面图标:

    有很多尺寸的图标(共15个不同命名的图标,尺寸可能有重叠部分),由 Contents.json 管理配置。

    2.修改 app 桌面名称:

    到 info.plist 当中修改。(虽然 flutter 号称跨平台框架,然而这部分 ios android 各自为政)。

    3.设置 app 启动画面:

    有几个尺寸命名,由 Contents.json 管理配置。不过这里也可以使 app 无视屏幕尺寸大小,统一使用一个图片。

    一、核心技术代码

    1.等价于 React 的 componentDidMount 的方法:

    需要使用 statefulWidgets 来实现

      @override
      void initState() {
        super.initState();
        WidgetsBinding.instance.addPostFrameCallback((_) => _didMount(context));
      }
    
    // 这里就是自定义的在 build 被调用完之后触发动作的地方
      _didMount(context) {
        Navigator.of(context).pushNamed("/choose-entry");
      }
    

    2.权限申请

    正规的 app 操作统一为在使用时询问用户是否予以功能使用授权,特殊页面用到时动态申请或者 app 第一次运行时申请。
    iOS 与 Android 是一样的步骤:
    Android 需要在 AndroidMenifest.xml 文件当中声明,然后再在运行时代码中写入申请动作;
    iOS 则是在 info.plist 当中先行注册,然后再在运行时代码中写入申请动作。
    全权限注册参照表

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>CFBundleDevelopmentRegion</key>
        <string>en</string>
        <key>CFBundleExecutable</key>
        <string>$(EXECUTABLE_NAME)</string>
        <key>CFBundleIdentifier</key>
        <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
        <key>CFBundleInfoDictionaryVersion</key>
        <string>6.0</string>
        <key>CFBundleName</key>
        <string>permission_handler_example</string>
        <key>CFBundlePackageType</key>
        <string>APPL</string>
        <key>CFBundleShortVersionString</key>
        <string>$(FLUTTER_BUILD_NAME)</string>
        <key>CFBundleSignature</key>
        <string>????</string>
        <key>CFBundleVersion</key>
        <string>$(FLUTTER_BUILD_NUMBER)</string>
        <key>LSRequiresIPhoneOS</key>
        <true/>
        <key>UILaunchStoryboardName</key>
        <string>LaunchScreen</string>
        <key>UIMainStoryboardFile</key>
        <string>Main</string>
        <key>UISupportedInterfaceOrientations</key>
        <array>
            <string>UIInterfaceOrientationPortrait</string>
            <string>UIInterfaceOrientationLandscapeLeft</string>
            <string>UIInterfaceOrientationLandscapeRight</string>
        </array>
        <key>UISupportedInterfaceOrientations~ipad</key>
        <array>
            <string>UIInterfaceOrientationPortrait</string>
            <string>UIInterfaceOrientationPortraitUpsideDown</string>
            <string>UIInterfaceOrientationLandscapeLeft</string>
            <string>UIInterfaceOrientationLandscapeRight</string>
        </array>
        <key>UIViewControllerBasedStatusBarAppearance</key>
        <false/>
    
        <!-- Permission options for the `location` group -->
        <key>NSLocationWhenInUseUsageDescription</key>
        <string>Need location when in use</string>
        <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
        <string>Always and when in use!</string>
        <key>NSLocationUsageDescription</key>
        <string>Older devices need location.</string>
        <key>NSLocationAlwaysUsageDescription</key>
        <string>Can I have location always?</string>
    
        <!-- Permission options for the `mediaLibrary` group -->
        <key>NSAppleMusicUsageDescription</key>
        <string>Music!</string>
        <key>kTCCServiceMediaLibrary</key>
        <string>media</string>
    
        <!-- Permission options for the `calendar` group -->
        <key>NSCalendarsUsageDescription</key>
        <string>Calendars</string>
    
        <!-- Permission options for the `camera` group -->
        <key>NSCameraUsageDescription</key>
        <string>camera</string>
    
        <!-- Permission options for the `contacts` group -->
        <key>NSContactsUsageDescription</key>
        <string>contacts</string>
    
        <!-- Permission options for the `microphone` group -->
        <key>NSMicrophoneUsageDescription</key>
        <string>microphone</string>
    
        <!-- Permission options for the `speech` group -->
        <key>NSSpeechRecognitionUsageDescription</key>
        <string>speech</string>
    
        <!-- Permission options for the `sensors` group -->
        <key>NSMotionUsageDescription</key>
        <string>motion</string>
    
        <!-- Permission options for the `photos` group -->
        <key>NSPhotoLibraryUsageDescription</key>
        <string>photos</string>
    
        <!-- Permission options for the `reminder` group -->
        <key>NSRemindersUsageDescription</key>
        <string>reminders</string>
    
    </dict>
    </plist>
    

    权限申请(.request())时,都是隐式检测如果已授权,则什么都不发生,如果未授权才会提示申请权限,当你需要在有/无权限时分别有其他动作的时候才会用得到权限状态判定。

    3.系统提示框调用

    目前没有任何方法可以调用出原生的提示框,只能使用flutter提供的Widgets实现。初步分析可能涉及到 flutter 调用原生接口的过程。

    4.本地数据存储与调用

    任务一:开发一个网站显示页的前置配置页(用于配置访问地址)

    5.使iOS应用允许http不安全通讯请求

    info.plist 里面直接添加

    
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>NSAppTransportSecurity</key>
        <dict>
            <key>NSAllowsArbitraryLoads</key>
            <true/>
        </dict>
    </dict>
    </plist>
    
    

    5.在Mac上部署flutter android开发环境时,中文未提到的细节

    • 如果报错,可能是使用的java版本有问题,找了好久才知道是该使用 java 8 ,不要搞错了版本。
    • 设置java环境变量,随便找个位置
    export JAVA_HOME=`/usr/libexec/java_home -v 1.8.0_241`
    

    注意尾部的版本号,可能与文中有所不同

    相关文章

      网友评论

        本文标题:【Flutter 手机 app 开发】

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