美文网首页Flutter
flutter项目升级2.0过程填坑记录

flutter项目升级2.0过程填坑记录

作者: 小话001 | 来源:发表于2021-07-15 16:42 被阅读0次

    在此之前先推荐看大佬的:填坑指导
    iOS需要注意:
    1、flutter2.0要求cocoapods 升级到1.9.0
    详情看这篇博客https://blog.csdn.net/sinat_31177681/article/details/51363495
    2、原来flutter项目中的podfile文件是旧版本的ccocoapods了,删除podfile和对应的.lock,然后flutter项目重新运行使用它自动生成的podfile文件
    3、安装CocoaPods
    卸载cocoapods:sudo gem uninstall cocoapods
    查看cocoapods版本:pod --version
    指定版本安装:
    sudo gem install -n /usr/local/bin cocoapods -v 1.9.3(新MacOS系统升级)
    不指定版本安装
    sudo gem install -n /usr/local/bin cocoapods


    说明:老项目sdk1.17.0===>升级到2.0.1,当前所有操作基于win平台

    (一)项目正式开始:

    1. 直接在SDK的安装目录运行命令
    //第一步
    git reset --hard c5a4b4029c0798f37c4a39b479d7cb75daa7b05c
    //第二步
    flutter doctor
    
    命令截图

    到此为止环境已经准备妥当,正式进入项目修改。

    2. 修改pubspec.yarm插件依赖

    所有的插件都要适配到空安全,插件是否支持均会有对应说明Null safety,适配过程不确定版本的话,可以使用dio: any,适配完事后再在pubspec.lock文件中查看具体的版本修改过来,实在有部分插件没有支持的,参考下面

    dependency_overrides:
      intl: ^0.17.0
    

    部分插件在适配空安全的版本放弃维护了,得自行更新或寻找替代,如:flutter_swiper变为flutter_swiper_null_safety,插件更新后要注意项目中的用法是否需要更新


    2.1 部分插件解决方案

    2.1.1:以前采用的是provide插件共享全局数据,现在变化为provider,用法改变,点击参考,以防文章丢失,我重复一遍:

    • provide 入口
    void main() {
      //顶层依赖
    var counter = Counter();
    var providers = Provider();
      providers
        ..(Provider<Counter>.value(counter))
      runApp(ProviderNode(child: MyApp(), providers: providers));
    }
    
    • provider 入口
    class MyApp extends StatelessWidget {
    var counter = Counter();
      @override
      Widget build(BuildContext context) {
        return MultiProvider(
          providers: [
           //这里是关键注册通知
            ChangeNotifierProvider(builder: (_) => counter),
          ],
          child:Text('xxx')
        );
      }
    }
    

    使用:

    • provide
    Provide.value<Counter>(context) .increment(调一下里面的方法。。这里也可以传参数进去只需要在 Counter 里面的 increment 里面写两个接受参数的就好);
    
    • provider
    Provider.of<Counter>(context, listen: false).increment(这里也可以传参数);
    

    比如:

    #注释掉的是我们”provide“ 的写法,这里主要是从我们建立的model里面取Id,因为Id是变化的。
    var formData = {
          "categoryId": Provider.of<ChildCategory>(context).categoryId, 
                      //Provide.value<ChildCategory>(context).categoryId,
          "categorySubId": categorySubId,
          "page": 1
        };
    
    • provide
    return Provide<CategoryGoodsListProvide>(builder: (context, child, data) {
    data.//也能点出我们设置好值
    }
    
    • provider
    final counter = Provider.of<Counter>(context);
        return Container(
          counter.//点出我们在model里面设置的值了
     );
    // 在使用 provider的时候我们要注意了一定要设置listen的Bool值就是这样
    await Provider.of<DetailsInfoProvide>(context, listen: false).getGoodsInfo(goodsId);
    

    2.1.2:dio版本升级到4.0.0最新版后,部分用法改变

    • 原来
    final Dio _dio = new Dio(options)
      ..interceptors.add(
        InterceptorsWrapper(onRequest: (RequestOptions options) {
          // 在请求被发送之前做一些事情
          options.headers['access-token'] = 'token';
          options.headers['last-stamp'] = '123';
          return options;
        }, onResponse: (Response response) async {
          // 在返回响应数据之前做一些预处理
          Fetch.onResponse(response);
          return response;
        }, onError: (DioError e) {
          // 当请求失败时做一些预处理
          Fetch.onError(e);
          return e;
        }),
      )
      ..interceptors.add(LogInterceptor(
          request: false,
          requestHeader: true,
          responseHeader: true,
          responseBody: true,
          requestBody: true));
     class Fetch{
       static void onResponse(response) async {
            if (response.request.method == 'POST') {
                   // 做一些操作
        }
       }
     }
    
    • 现在
    final Dio _dio = new Dio(options)
      ..interceptors.add(
        InterceptorsWrapper(onRequest: (RequestOptions options,requestInterceptorHandler) {
          // 在请求被发送之前做一些事情
          options.headers['access-token'] = 'token';
          options.headers['last-stamp'] = '123';
          return requestInterceptorHandler.next(options);
        }, onResponse: (Response response,responseInterceptorHandler) async {
          // 在返回响应数据之前做一些预处理
          Fetch.onResponse(response);
          return responseInterceptorHandler.next(response);
        }, onError: (DioError e,errorInterceptorHandler) {
          // 当请求失败时做一些预处理
          Fetch.onError(e);
         return errorInterceptorHandler.next(e);
        }),
      )
      ..interceptors.add(LogInterceptor(
          request: false,
          requestHeader: true,
          responseHeader: true,
          responseBody: true,
          requestBody: true));
     class Fetch{
       static void onResponse(response) async {
            if (response.requestOptions.method == 'POST') {
                   // 做一些操作
        }
       }
     }
    

    2.2 部分报错

    2.2.1

    报错1
    解决方案:打开android目录下的app文件夹下的AndroidManifest.xml文件,在activity标签的下面增加如下代码:
    <meta-data
               android:name="flutterEmbedding"
               android:value="2"/>
    

    2.2.2

    Missing concrete implementations of 'CupertinoLocalizations.tabSemanticsLabel', 'getter CupertinoLocalizations.modalBarrierDismissLabel', 'getter CupertinoLocalizations.searchTextFieldPlaceholderLabel', 'getter CupertinoLocalizations.timerPickerHourLabels', and 2 more.
    Try implementing the missing methods, or make the class abstract.

    解决方案:

    // 当前类下添加覆盖方法
      @override
      String get modalBarrierDismissLabel => throw UnimplementedError();
    
      @override
      String get searchTextFieldPlaceholderLabel => throw UnimplementedError();
    
      @override
      String tabSemanticsLabel({int tabIndex, int tabCount}) {
        throw UnimplementedError();
      }
    
      @override
      List<String> get timerPickerHourLabels => throw UnimplementedError();
    
      @override
      List<String> get timerPickerMinuteLabels => throw UnimplementedError();
    
      @override
      List<String> get timerPickerSecondLabels => throw UnimplementedError();
    

    2.2.3

    The argument type 'Duration' can't be assigned to the parameter type 'Future<Duration>'

    解决方案:

          future: entity.videoDuration,
        更换为 Future.delayed(Duration(seconds: 0)),
    

    2.2.4

    The method 'inheritFromWidgetOfExactType' isn't defined for the type 'BuildContext'.
    Try correcting the name to the name of an existing method, or defining a method named 'inheritFromWidgetOfExactType'

    解决方案:

    context.inheritFromWidgetOfExactType(ConfigProvider);
    更换为      static ConfigProvider of(BuildContext context) =>
          context.dependOnInheritedWidgetOfExactType(aspect:ConfigProvider);
    

    2.2.5

    The named parameter 'resizeToAvoidBottomPadding' isn't defined.
    Try correcting the name to an existing named parameter's name, or defining a named parameter with the name 'resizeToAvoidBottomPadding'

    解决方案:

          resizeToAvoidBottomPadding: false,
      更换为      resizeToAvoidBottomInset: false,
    

    2.2.6

    pin_input_text_field新版用法:
    The named parameter 'enteredColor' isn't defined.
    Try correcting the name to an existing named parameter's name, or defining a named parameter with the name 'enteredColor'

    解决方案:

    PinDecoration _pinDecoration = UnderlineDecoration(
     lineHeight: 1.0,
          enteredColor: Color(0xffe5e5e5),
          color: Color(0xffe5e5e5));
    )
    更换enteredColor、color为:
    colorBuilder:FixedColorBuilder(Color(0xffe5e5e5)
    

    2.2.7

    The method 'PinEditingController' isn't defined for the type '_CodeLoginState'.
    Try correcting the name to the name of an existing method, or defining a method named 'PinEditingController'

    解决方案:

    //原来定义的方式
      PinEditingController _pinEditingController = PinEditingController(
        pinLength: _pinLength,
        autoDispose: false,
      );
    更换为
    TextEditingController _pinEditingController = TextEditingController(text: '');
    使用的地方将pinEditingController换为controller,将inputFormatter换为inputFormatters
    

    2.2.8

    SliverOverlapAbsorber 组件child 属性移除,换新属性;
    The named parameter 'child' isn't defined.
    Try correcting the name to an existing named parameter's name, or defining a named parameter with the name 'child'

    解决方案:child 换为sliver
    2.2.8.1

    uses-sdk:minSdkVersion 17 cannot be smaller than version 18 declared in library

    解决方案:项目目录下: android--app-build.gradle --minSdkVersion改为:18 或者19
    2.2.8.2

    Publishable packages can't have path dependencies.
    Try adding a 'publish_to: none' entry to mark the package as not for publishing or remove the path dependency

    解决方案:在pubspec.yarm管理里面添加:publish_to

    description: 测试demo
    publish_to: none
    

    2.2.8.3

    The getter 'initialized' isn't defined for the type 'VideoPlayerValue'.

    解决方案:video_player升级后字段发生了变化,initialized字段更换为:isInitialized(_controller.value.isInitialized)
    2.2.8.4

    Undefined name 'AudioPlayerState'.Try correcting the name to one that is defined, or defining the name

    解决方案:

    AudioPlayerState _playerState = AudioPlayerState.STOPPED
    // video_player升级后字段变化, 变更为
    PlayerState _playerState = PlayerState.STOPPED
    

    2.2.8.5

    FlutterEngine GeneratedPluginRegistrant.registerWith(this);

    解决方案:

    //方案一:在AndroidManifest.xml添加
    <meta-data android:name="flutterEmbedding" android:value="2"/>
    
    //方案二
    将GeneratedPluginRegistrant.registerWith(new FlutterEngine(this));或GeneratedPluginRegistrant.registerWith(this);
    //替换为
     GeneratedPluginRegister.registerGeneratedPlugins(FlutterEngine(this));
    或GeneratedPluginRegistrant.registerWith(FlutterEngine(this))
    
    //方案三:注释掉:
     //import io.flutter.app.FlutterActivity;
    //   GeneratedPluginRegistrant.registerWith(this);
     // MethodChannel methodChannel = new MethodChannel(getFlutterView(), CHANNEL);
    改为:
     import io.flutter.plugins.GeneratedPluginRegistrant;
     import io.flutter.embedding.engine.FlutterEngine;
     import io.flutter.embedding.android.FlutterActivity;
    
    public class MainActivity extends FlutterActivity {
        private static final String CHANNEL = "com.baidu.app";
        private MethodChannel mMethodChannel; // 
        String multibarcode = ""; // 批量扫描码
        MyCodeReceiver receiver = new MyCodeReceiver();
    
    //新添加    
    @Override
        public void configureFlutterEngine(FlutterEngine flutterEngine){
            GeneratedPluginRegistrant.registerWith(flutterEngine);
        }
    //改变
      protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // GeneratedPluginRegistrant.registerWith(this);
            // MethodChannel methodChannel = new MethodChannel(getFlutterView(), CHANNEL);
            MethodChannel methodChannel = new MethodChannel(getFlutterEngine().getDartExecutor().getBinaryMessenger(), CHANNEL);
            methodChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
                  @Override
                public void onMethodCall(MethodCall call, MethodChannel.Result result) {
                }
           }
     }
    
    }
    

    2.2.8.6

    Invalid Podfile file: no implicit conversion of nil into String.

    解决方案: 方案一:删除ios目录下的Podfile.lock 文件然后重新运行 pod install命令
    方案二:删除ios目录下的Podfile.lock与Podfile文件 重新运行flutter run或flutter build ios
    方案三:删除ios目录,重新运行flutter create .命令,注意有"."这个符号不要忘记
    2.2.8.7

     Using `ARCHS` setting to build architectures of target `Pods-Runner`: (``)
    

    这个报错一般对应的就是下面的报错,注意看后面的报错信息,看是哪个插件报错。

    Specs satisfying the photo_manager (from.symlinks/plugins/photo_manager/ios) dependency were fo

    解决方案:把Podfile的版本注释打开,改为platform :ios, '9.0' 或者是更高的版本


    2.3 部分警告

    全局替换
    1.将new List() 替换为[];
    2.TextField的inputFormatters:[WhitelistingTextInputFormatter.digitsOnly] 替换为[FilteringTextInputFormatter.digitsOnly]
    3.TextField的inputFormatters:[WhitelistingTextInputFormatter(RegExp("[a-z|A-Z|0-9]"))]替换为FilteringTextInputFormatter.allow(RegExp("[a-z|A-Z|0-9]"))
    4.Stack组件中overflow: Overflow.visible改为 clipBehavior: Clip.none;overflow: Overflow.clip改为clipBehavior:Clip.hardEdge
    5.ListWheelScrollView组件中clipToSize = false改为clipBehavior: Clip.none,clipToSize = true改为 Clip.hardEdge
    6.TextField中maxLengthEnforced: true改为maxLengthEnforcement:MaxLengthEnforcement.enforced
    7.FlatButton、RaisedButton、OutlineButton的变化:官方参考
    颜色的属性发生了变化,由原来的Color 变为了MaterialStateProperty<Color>, 这是未了解决不同状态(pressed、hovered、focused、disabled)下按钮颜色的变化
    例如

    ElevatedButton(
         style: ButtonStyle(
         backgroundColor: MaterialStateProperty.resolveWith<Color>(
           (Set<MaterialState> states) {
            //此处根据按钮不同的状态可设置不同颜色
           if (states.contains(MaterialState.pressed))
               return Colors.red.withOpacity(1);
               return null; // 默认的背景颜色.
             },
           ),
        ),
       child: Text('取消'),
       onPressed: () => print('object'),
    )
    
    • 之前:FlatButton
    FlatButton(
          hoverColor: Colors.cyan,
          focusColor: Colors.pink,
          color:Colors.green,//按钮的颜色
          splashColor:Colors.yellow,//点击按钮时水波纹的颜色(会闪一下的那个颜色)
          highlightColor: Colors.red,//点击(长按)按钮后按钮的颜色
          textColor: new Color(0xff75b9ff), //文字颜色
          child: new Text('取消'),
          onPressed: () {
             print('xxxx');
          },
    ),
    
    • 现在:TextButton
     //官方版(推荐)
      TextButton(
      style: ButtonStyle(
     // 分别对应上面的focusColor、hoverColor、splashColor
        overlayColor: MaterialStateProperty.resolveWith<Color>(
          (Set<MaterialState> states) {
            if (states.contains(MaterialState.focused))
              return Colors.pink;
            if (states.contains(MaterialState.hovered))
                return Colors.cyan;
            if (states.contains(MaterialState.pressed))
                return Colors.yellow;
            return null; // Defer to the widget's default.
        }),
      ),
      onPressed: () { },
      child: Text('TextButton with custom overlay colors'),
    )
    //自己版
                    TextButton(
                        style: ButtonStyle(
                          backgroundColor: MaterialStateProperty.resolveWith<Color>(
                              (Set<MaterialState> states) {
                            if (states.contains(MaterialState.pressed))
                              return Colors.red.withOpacity(1);
                            return Colors.green; 
                          }),
                          foregroundColor:
                              MaterialStateProperty.all<Color>(Color(0xff75b9ff)),
                        ),
                        onPressed: () => print('object'),
                        child: Text('取消'))
    // 简单版
    TextButton(
      style: TextButton.styleFrom(
        primary: Colors.blue,//文字颜色
        backgroundColor:Colors.green,//背景色
        onSurface: Colors.red,// 禁用时候效果
      ),
      onPressed: null,
      child: Text('TextButton'),
    )
    
    • 之前:RaisedButton
    RaisedButton(
        padding: EdgeInsets.symmetric( vertical: 20, ),
        textColor: Colors.white,
        color: Color(0xff006db8),
        highlightColor: Color(0xff067bcb),
        highlightElevation: 5,
        shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10)),
        child: Text(  '保存', style: TextStyle( fontSize: 10,)),
        onPressed: () => print('ss'),
     ),
    
    • 现在:ElevatedButton
     ElevatedButton(
        // style: ElevatedButton.styleFrom(
        //   onPrimary: Colors.white,  //文字色
       //   primary: Color(0xff006db8),//背景色
      //   padding: EdgeInsets.symmetric(vertical: 20),//文字与按钮的间距
       //   minimumSize: Size(88, 36),//按钮最小宽高
    //      RoundedRectangleBorder( borderRadius: BorderRadius.circular(10))
       //   elevation:5 //按钮阴影
        // ),
     style: ButtonStyle(
     backgroundColor: MaterialStateProperty.resolveWith<Color>(
            (Set<MaterialState> states) {
                  if (states.contains(MaterialState.pressed))
                       return Color( 0xff067bcb); //采用这种和overlayColor 效果于原来splashColor稍微有点点区别
                              return Color(0xff006db8); // 默认的背景颜色.
                            },
                          ),
      foregroundColor:MaterialStateProperty.all<Color>(Color(0xffffffff)),
      padding: MaterialStateProperty.all<EdgeInsetsGeometry>(EdgeInsets.symmetric(vertical: 20)),
      minimumSize: MaterialStateProperty.all<Size>(Size(88, 36)),
      elevation: MaterialStateProperty.all(5)),
      shape: MaterialStateProperty.all<OutlinedBorder>(RoundedRectangleBorder( borderRadius:BorderRadius.circular(10))),
      child: Text( '保存', style: TextStyle(fontSize: 10, ),),
      onPressed: () => print('ss'),
    ),
    

    8.出现如下警告

    The <style> element is not implemented in this library.
    Style elements are not supported by this library and the requested SVG may not render as intended.
    If possible, ensure the SVG uses inline styles and/or attributes (which are supported), or use a
    preprocessing utility such as svgcleaner to inline the styles for you.
    解决方案:flutter_svg插件升级到新版后,以前的svg图片不能使用,需要下载https://github.com/RazrFalcon/svgcleaner-gui/releases
    下载后直接运行,import导入svg图片,然后点击run处理即可,用新的svg图片替换原来的svg图

    9.showSnackBar报错误

    'showSnackBar' is deprecated and shouldn't be used. Use ScaffoldMessenger.showSnackBar

    解决方案:Scaffold换为ScaffoldMessenger
    10.textSelectionColor弃用

    Use TextSelectionThemeData.selectionColor instead

    解决方案:

    // textSelectionColor: Color(0xff2294E2)
    //更换为下面用法
    textSelectionTheme:TextSelectionThemeData(selectionColor:Color(0xff2294E2))
    

    11.charts_flutter升级后属性报错

    The constructor returns type 'AxisSpec<dynamic>' that isn't of expected type 'NumericAxisSpec'

    解决方案:

     primaryMeasureAxis: charts.AxisSpec(showAxisLine: true),
    //更换为
     primaryMeasureAxis: new charts.NumericAxisSpec(showAxisLine: true),                  
    

    12.flutter 真机调试无法访问网络,dio报错

    Insecure HTTP is not allowed by platform

    解决方案:
    android:

    // android/app/src/main 文件夹
    <application
        ...
        android:networkSecurityConfig="@xml/network_security_config"
        android:usesCleartextTraffic="true"
        ...   >
    
    在android/app/src/main/res/xml/network_security_config.xml 写下
     <?xml version="1.0" encoding="utf-8"?>
    <network-security-config>
        <domain-config cleartextTrafficPermitted="true">
            <domain includeSubdomains="true">common.baidu.net</domain>
            <domain includeSubdomains="true">common.baidu.cn</domain>
            <domain includeSubdomains="true">common.baidu.com</domain>
            <domain includeSubdomains="true">video.baidu.com</domain>
        </domain-config>
         <base-config cleartextTrafficPermitted="true">
            <trust-anchors>
                <certificates src="system" />
            </trust-anchors>
        </base-config>
    </network-security-config>
    

    ios:

    //info.plist下添加
    <key>NSAppTransportSecurity</key>
    <dict>
      <key>NSAllowsArbitraryLoads</key>
          <true/>
    </dict>
    

    问题12完整参考

    相关文章

      网友评论

        本文标题:flutter项目升级2.0过程填坑记录

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