<flutter>跨平台开发小白入坑 Dart Dio Pubspec 打包 MethodChannel 解析 Xcode hybrid

1.资源文件和依赖三方包(pubspec.yaml)

pubspec.yaml文件可以说是和安卓的gradle文件差不多,它用来描述版本号、sdk、依赖等的。

在资源导入方面同安卓不一样的是,flutter需要在pubspec.yaml中声名,不然在dart文件中是引用不到的,但是pubspec.yaml中不论是资源声名还是依赖都需要固定格式,例如:

 就类似这样,有几个空格、对不对齐都决定着你是不是能依赖和声明成功。

我们可以对资源声明目录,但是批量指定并不递归,只有在该目录下的文件才可以被包括,如果下面还有子目录的话,需要单独声明子目录下的文件。

2.适配

图片资源适配也是和安卓差不多,分为2.0x,3.0x,4.0x,系统会自动根据屏幕选择最接近的分辨率,这里需要注意的是,比如说目录是在assets/images/下面创建的分辨率包,那images根目录下就是1.0x的分辨率,而且这里也必须要有对应的资源,因为它是资源的标识符。

 字体大小和宽高等的适配也有是对应框架的,我们可以添加依赖:flutter_screenutil: ^0.4.2,然后在入口里对它添加对应的设计尺寸进行初始化:

ScreenUtil.instance = ScreenUtil(width: 375, height: 893)..init(context);

 然后在dart里使用ScreenUtil.setXXX就可以了。

3.网络请求和json解析

经常用到的应该就是dio了,它的请求贴下代码吧:

  static Future<Map<String, dynamic>> requestPost<T>(
      String url, Map<String, String> map) async {
    String path = HttpConfig.baseUrl + url;
    //添加头部信息
    BaseOptions options = BaseOptions();
    options.headers["Accept-Encoding"] = "gzip";
    options.headers["Accept"] = "application/json";
    options.contentType = "application/json; charset=utf-8";
    // options.headers["User-Agent"] = getUserAgent();
    options.connectTimeout = HttpConfig.timeOut;
    options.headers["Authorization"] = "Bearer";

    String currentTimeMillis =
        DateTime.now().microsecondsSinceEpoch.toString().substring(0, 10);

    //公参配置
    map["sign"] = "";
    map["device_code"] = device_code;
    map["timestamp"] = currentTimeMillis;
    map["uid"] = uid;
    Dio dio = Dio(options);
    //添加拦截
    dio.interceptors.add(
      InterceptorsWrapper(
          onRequest: (RequestOptions options) {

          },
          onResponse: (Response response) {
            //拦截处理后台的错误格式数据
            var errData= response.data.toString();
             xxx ...
            return response;
          },
          onError: (DioError dioError) {

          }),
    );
    Response response = await dio.post(path);
    var str = jsonDecode(response.data); //处理一下返回Future数据
    return str;
  }

请求很简单,我想讲的主要点是解析数据,我用到了json_annotation,一开始我是用了网络大家都推荐的FlutterJsonBeanFactory 插件,插件中心下载安装后发现创建File->New>JsonToDartAction里面的JsonToDartAction点击没有任何反应,最后怀疑就是因为as版本或者其他什么环境的问题,那捷径没走成,那就找到了另一个工具插件JsonToDartClass,它的用法流程其实和所谓的jsonFormart差不多,首先新创建一个dart类,然后在类里右键选择Generate,点击JsonToDartClass,会出现下图:

 将正确无误的json格式的json数据填入里面点ok(注意是正确格式的json,否则会出错),我们就会生成这么一个类:

接着我们会发现报错了,那是因为我们还有一步操作,也是容易出问题的地方,这里就和咱们原生安卓不一样了,我们需要在项目根目录下运行命令:

flutter packages pub run build_runner build,如果你运气够好,那会在你这个dart文件的同级文件夹内生成一个.g.dart的文件,此时所有的报错就都会消失了,这时候我们就需要:

var datas = JSDataBean.fromJson(result); 来获取到data就可以各种赋值了。

但是上边说运气好的情况,运气不好,你就会遇到以下问题:

1.因为json格式有误导致的生成出错,改正格式即可

2.有些报错(比如:Failed to build build_runner:build_runner:)发现和build_runner的版本号有关系,需要调整版本号

3.报错Flutter Conflicting outputs were detected and the build is unable to prompt for permiss,这个时候是生成的dart_tool目录下缺少了之前生成的文件导致的报错,可以依次执行下边命令即可:
1、flutter packages pub run build_runner clean
2、flutter packages pub run build_runner build --delete-conflicting-outputs
3、flutter packages pub run build_runner build

当然也有一劳永逸的办法,运行flutter packages pub run build_runner watch可以以后自动生成。

4.打包体验流畅的app

说到flutter都知道debug版本可以热更,HotReload和HotRestart,只需要ctril+s就可以直接预览界面,非常的方便有效率,由于debug和relase编译机制不一样(debug是jit即时编译,relase是aot提前编译),所带来的问题就是debug版本性能阉割太厉害,调试的时候可以感受得到卡的像老年机,那我们打正式包:

1.生成key文件:执行命令:

keytool -genkey -v -keystore E:\_as_project_me\flutter_app\flutter_app_key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key

此时会提示让填入各种信息,依次填写就行,完成后就可以生成key文件了。

2.配置安卓gradle

signingConfigs {
    release {
        keyAlias 'key'
        keyPassword 'xxx'
        storeFile file('E:\\_as_project_me\\flutter_app\\flutter_app_key.jks')//此种写法默认key文件在android-app文件夹下
        storePassword 'xxx'
    }
    debug {
        keyAlias 'key'
        keyPassword 'xxx'
        storeFile file('E:\\_as_project_me\\flutter_app\\flutter_app_key.jks')//此种写法默认key文件在android-app文件夹下
        storePassword 'xxx'
    }
}

3.打包执行命令:flutter build apk,如果报安全问题,则执行:flutter build apk --no-sound-null-safety 即可。

 关于苹果打包,我也尝试装了个黑苹果,安装了xcode和as导入项目,不过还没获得苹果开发者账号,所以没把流程走到最后,有个文章可以大家参考下:

Flutter IOS 真机调试,无需开发者账号! - Kxmrg的开发日记

** 关于打包后在手机上运行正式包可能或多或少也会出现问题,那么我这次打包遇到了两个debug和relase版本运行情况不一致的情况:

(1). 吐司报错:SDK初始化失败,请检查是否集成umeng-asms-1.2x.aar库,或者分享sdk必须配合友盟基础组件库v9.2.x版本...   只需要关闭混淆即可解决: useProguard false

(2). 界面明明在debug上没任何问题,一打包部分页面不显示了,变成了灰色背景,这个问题主要是对组件的用法不严谨导致的,一般有两点地方:第一个就是布局的最外层不能使用stack布局,需要在stack外部套上一层column或者row;第二是expand只能用在row和colum下,不能再container下使用。

如果你遇到了上边这些问题,那赶快去检查一下吧!

5.关于flutter和原生交互 hybrid

flutter提供了三种交互方式,分别是MethodChannel、BasicMessageChannel、EventChannel,这里我们详细介绍MethodChannel这种方式。下面以微信分享和支付来做示例:

首先是安卓端:

class MainActivity : FlutterActivity() {
    private val CHANNEL = "samples.flutter.io/lib"
    lateinit var wxPayResult: MethodChannel.Result
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        init();
        // 使用MethodChannel进行通信
        val binaryMessenger = getFlutterEngine()?.getDartExecutor()?.getBinaryMessenger();
        if (binaryMessenger != null) {
            MethodChannel(binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> // 使用MethodCall进行方法名匹配
                when (call.method) {
                    "UmGetWxInfo" -> {
                        UmGetWxInfo(result);
                    }
                    "shareWX" -> {
                        shareWX(call.arguments as HashMap<String, String>);
                    }

                    "wxPay" -> {
                        wxPay(call.arguments as HashMap<String, String>, result);
                    }

                    else -> {
                        result.notImplemented()
                    }
                }
            }
        }
    }

    /**
     * 支付宝支付
     */
    fun aliasPay(payStr: String, result: MethodChannel.Result) {
        result.success("回调结果")
            ...
    }

    /**
     * 微信支付
     */
    fun wxPay(map: HashMap<String, String>, result: MethodChannel.Result) {
        result.success("回调结果")
            ...
    }

    /**
     * 微信登录
     */
    fun UmGetWxInfo(result: MethodChannel.Result) {
        result.success("回调结果")
            ...
    }

    /**
     * 初始化
     */
    fun init() {
       ...
    }

}

然后是flutter端:

  static const platform = const MethodChannel('samples.flutter.io/lib');

  /**
   * 微信登录
   */
  wxLogin(BuildContext context) async {
    try {
      final String result = await platform.invokeMethod('UmGetWxInfo'); //获取原生数据
      var str = jsonDecode(result);
      var wxLoginBean = WxLoginBean.fromJson(str);
      Map<String, String> objectObjectHashMap = Map<String, String>();
      objectObjectHashMap["city"] = wxLoginBean.city;
      objectObjectHashMap["country"] = wxLoginBean.country;
      objectObjectHashMap["headimgurl"] = wxLoginBean.iconurl;
      objectObjectHashMap["language"] = wxLoginBean.language;
      objectObjectHashMap["nickname"] = wxLoginBean.name;
      objectObjectHashMap["openid"] = wxLoginBean.openid;
      objectObjectHashMap["province"] = wxLoginBean.province;
      objectObjectHashMap["sex"] = wxLoginBean.gender == "男" ? "1" : "2";
      objectObjectHashMap["unionid"] = wxLoginBean.unionid;
      var results = await HttpRequest.requestPost(
          UrlConfig.WXLOGIN_URL, objectObjectHashMap);
      var datas = UserInforDataBean.fromJson(results);
      Toast.makeToast(context, "登录成功");
      EventbusUtil.eventBus.fire(UserRefreshEvent(1));
      Navigator.pop(context);
      WatPreferencesUtil.setUserInfo(datas);
    } on PlatformException catch (e) {
      Toast.makeToast(context, "登录失败");
    }
  }

下面给大家展示一下可以传递的一些数据类型:

6.dart的语法等一些细节

1.expand:填充满剩余空间,比如:children中的子控件想要拉伸,例如spaceBetween这种的,需要对children外部的Container嵌套一个Expanded才行,先把外部容器拉伸。

2.SingleChildScrollView嵌套容器时候 如果是listview则设置   physics:NeverScrollableScrollPhysics(),  shrinkWrap: true,    否则就给父组件设置固定高度:constraints: BoxConstraints(maxHeight: 100),

3.NestedScrollView 同安卓 折叠组件SliverAppBar

4.ListView中Axis.horizontal时,需要父view指定高度

5.在GridView中的元素无法设置其宽高,主要通过childAspectRatio来设置其比例,通过比例来显示其大小

6.显示省略号overflow: TextOverflow.ellipsis

7.如果你适应了java的new对象写法,写一个new Container时,他会有黄色警告,去掉new就好了

8.const:当一个变量在编译时就确定而且以后不会再发生改变的时候,我们将使用const来修饰,节省build时间

9.color 和 decoration 是互斥的,如果同时设置它们会报错,因为当指定 color 属性时,在 Container 内会自动创建一个 decoration。

10.bottomNavigationBar中,onTab切换会把页面重置,这时候需要页面继承AutomaticKeepAliveClientMixin ,重写 @override   bool get wantKeepAlive => true; 可解决。

11.late JSDataBean jsData;为稍后初始化,可去掉强制让初始化的限制 ,在使用前被初始化

12  .?(类型后面跟操作符 ? 表示当前变量可为null。)
        !(在使用可为null的变量时对可为null变量的另一种处理方式)

13.Stack相当于RelativeLayout,children里面层叠,要居中给Stack设置alignment

14. 如果一个dart页面中导入两个bean类,而两个bean类都有同一字段,则需要修改其中一个,不然会报错的。

15.dart有一个可选参数概念,就是构造函数可以加个花括号,就可以在传参进行选择性传递;

16.单纯的dart是支持反射机制的,但是flutter把他禁用了,由于反射默认会使用所有的代码,就导致在发布应用的时候没法去除掉未使用的代码,没法显著的优化程序的大小,所以Flutter禁用了Dart的反射机制。

17.text组件有时候文字下面有两条黄色线 ,这时候有可能你用的是MaterialApp,修改Scaffold或者Material做父组件即可。

18.flutter特别的地方:死亡红屏、黄色越界

19.如果build里面用到对象的属性,那build之前必须初始化对象,要么用late,要么用空类型,二者不可并用,可以判空数据展示loadingview,有数据了再展示数据。

.........

有时间会再更新,不足之处多多指正。

猜你喜欢

转载自blog.csdn.net/csdn_lg_one/article/details/128457621