Flutter 开发笔记

一、前言

最近在使用Flutter开发新项目,但是有很多小的使用点很容易遗忘,这里做下笔记,以备下次使用时查阅

二、小笔记汇总

  • 1、键盘回收与弹出
    1、点击空白处回收键盘是很多界面都需要的需求,下面是实现方法
/// 在body上添加一个手势控件,实现键盘回收
@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GestureDetector(
        onTap: (){
          // 键盘回收
          FocusScope.of(context).unfocus();
        },
        child:Container()
      )
    );
  }

2、进入界面键盘主动弹出

TextField(
    style: TextStyle(fontSize: 20),
    controller: _textController,
    keyboardType: TextInputType.number,
    autofocus: true, // 设置为true则主动弹出
    inputFormatters: [ // 限制输入
    LengthLimitingTextInputFormatter(6),
    WhitelistingTextInputFormatter.digitsOnly
    ],
   decoration: InputDecoration(
       hintText: "键盘主动弹出",
       border: InputBorder.none, // 去掉下滑线
       contentPadding: EdgeInsets.all(0), // 居中
      ),
),        
  • 2、延时执行
// 延时1s执行返回
Future.delayed(Duration(seconds: 1), (){
   Navigator.of(context).pop();
});
  • 3、界面返回问题
    需求:禁用掉iOS侧滑返回功能,监听安卓虚拟按键,监听返回按钮
    WillPopScope,在fultter 中专门拦截返回导航键,我们的操作都是基于他的
    使用了这个控件之后,iOS中的侧滑返回会失效,Nav上的返回事件和安卓虚拟返回按键都会监听到,可以在里面做想要的处理,例如,点击两次退出app,回传数据等
/// 控件介绍
 const WillPopScope({
    Key key,
    @required this.child, // 可以将界面写到这里
    @required this.onWillPop, // 在这里处理返回事件
  }) : assert(child != null),
       super(key: key);

/// 具体用法
/// .........
@override
  Widget build(BuildContext context) { 
    return WillPopScope(
        child: Scaffold(
          body: Container(
                child: Text('WillPopScope使用介绍')
          ),
        ),
        onWillPop: _requestPop
    );
  }

// 手动处理返回事件
  Future<bool> _requestPop() async{
    Navigator.pop(context);//返回上一个界面
    return Future.value(false); // false 则不退出,true 退出
  }
  • 4、退出应用
    SystemNavigator.pop()
    使用上述3中的WillPopScope监听返回按钮,在onWillPop中处理退出问题。
/// 导入包
import 'package:flutter/services.dart';
/// .........
@override
  Widget build(BuildContext context) { 
    return WillPopScope(
        child: Scaffold(
          body: Container(
                child: Text('退出应用')
          ),
        ),
        onWillPop: _requestPop
    );
  }
/// 返回按钮点击事件
 Future<bool> _requestPop() async{
    SystemNavigator.pop();;//退出应用
    return Future.value(false); 
  }
  • 5、字体去除下滑线
    有时候在一些Widget中,字体会默认自带下滑线,去除的方法是在style属性中设置decoration
Text('字体去除下滑线',style: TextStyle( decoration: TextDecoration.none))
  • 6、获取系统语言
    在做国际化的时候会需要获取当前的系统语言和地区信息,框架有提供方法,我们可以用下面方法获取语言和地区
// 获取本地语言类型
Locale locale = Localizations.localeOf(context);
String localLanguage =  locale.languageCode; // 语言 en-英文 zh-中文
String localCountry = locale.countryCode; // 地区
  • 7、设置版本号不生效
    Flutter 版本号控制在 pubspec.yaml 里面,
    version: 1.0.1+1 #版本号和buildNumber
    设置后不生效执行清除后重新安装
1、修改后执行flutter get
2、执行flutter clean
3、重新build安装
  • 8、打包
    iOS打包时先在项目路径下执行如下命令,执行命令的时候Xcode保持关闭,执行结束后再打开Xcode打包
flutter clean
flutter build ios --release

Android打包可以直接在项目中打包,配置好密钥等信息后直接执行,注意storeFile路径中不能有中文

// 默认使用系统的key,位置为/User/xxxx/.android/debug.keystore
flutter build apk
  • 9、判断平台
import 'dart:io';

void getPlatform(){
  if (Platform.isIOS){

  }else if (Platform.isAndroid){

  }
}
  • 10、TextField设置高度后hintText居中
    TextField设置高度小于默认高度后会出现hintText不居中的情况,经过一番摸索后发现InputDecoration中isCollapsed设置true即可
TextField(
      decoration: InputDecoration(
                hintText: "用户名或邮箱",
                border: InputBorder.none, // 去掉下滑线
                isCollapsed:true
),
  • 11、图片设置圆角
    实现图片圆角有多种办法,但是这种是目前发现最简单的一种
ClipRRect (
         borderRadius: BorderRadius.circular(4),
         child: Image.asset("images/header.png"),
    ),
  • 12、防止键盘弹出时整个界面上移
    resizeToAvoidBottomPadding 设置为false
return Scaffold(
  appBar: AppBar(
    elevation: 0.0,
    title: new Text("下面一行代码防止整个界面上移"),
  ),

  resizeToAvoidBottomPadding: false, //输入框抵住键盘
);

  • 13、AlertDialog内容无法经过setState更新
1、把Dialog的content单独拿出来,单独用一个Stateful Widget包裹来,
用独立的setState绕过Dialog实现控制内部动态组件的刷新。

2、弹窗控件内部StatefulBuilder 包裹也可以实现更新,
参考:https://blog.csdn.net/qq_39081974/article/details/109097324
  • 14、文本和图标一行展示
    image.png
Container(
      padding: EdgeInsets.only(top: ScreenUtil().size(10), bottom: ScreenUtil().size(10)),
      child: Text.rich(
        TextSpan(
          style: TextStyle(fontSize: ScreenUtil().font_15, color: ColorsUtil.blueColor),
          children: [
            WidgetSpan(
              child: Image.asset('images/info/icon.png', width: ScreenUtil().size(20), height: ScreenUtil().size(20)),
            ),
            TextSpan(
              text: "你好这样就可以带着图标一块换行了呀",
            ),
          ],
        ),
      ),
    );
  • 15、键盘弹出底部布局跟随移动问题

解决思路:监听键盘弹出,弹出时将底部布局置为空布局

方法1:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class KeyboardPage extends StatefulWidget {
  const KeyboardPage({Key? key}): super(key: key);

  @override
  State<KeyboardPage> createState() => _KeyboardPageState();
}

class _KeyboardPageState extends State<KeyboardPage> with WidgetsBindingObserver {
  double _keyboardHeight = 0;
  
  @override
  void initState() {
    super.initState();
    //2.添加监听
    WidgetsBinding.instance.addObserver(this);
  }

  //3.覆盖监听方法
  @override
  void didChangeMetrics() {
    super.didChangeMetrics();
    //4.获取跟布局与底部的高度
    final double keyboardHeight =
        WidgetsBinding.instance.window.viewInsets.bottom;
    debugPrint("键盘高度变化" + keyboardHeight.toString());
    //5.更新高度
    setState(() {
      _keyboardHeight = keyboardHeight;
    });
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: SafeArea(
          child: GestureDetector(
            onTap: () {
              // 键盘回收
              FocusScope.of(context).requestFocus(FocusNode());
            },
            child: Container(
              alignment: Alignment.centerLeft,
              width: double.infinity,
              child: Column(
                children: [
                  _buildTopWidget(),
                  Expanded(
                      child: ListView(
                        children: [
                          _buildInfoWidget(),
                        ],
                      )),
                  _keyboardHeight > 0 ? Container() :_buildBottomWidget()
                ],
              ),
            ),
          ),
        ));
  }

  Widget _buildTopWidget() {
    return Container(
      // 隐藏代码
    );
  }

  Widget _buildInfoWidget() {
    return Container(
      // 隐藏代码
    );
  }
  
  /// 底部协议和按钮
  Widget _buildBottomWidget() {
    return Container(
      // 隐藏代码
    );
  }
}

方法2:bool isKeyboardVisible = MediaQuery.of(context).viewInsets.bottom > 100进行简单处理

@override
Widget build(BuildContext context) {
  bool isKeyboardVisible = MediaQuery.of(context).viewInsets.bottom > 100;
  return BasePageScaffold(
    title: "键盘弹出问题",
    body: Column(
      children: [
        Expanded(child: StyleTextField(hintText: "请输入内容")),
        if (!isKeyboardVisible)
          Container(
            padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
            child: CustomButton(title: "按钮", height: scaleSize(40)),
          )
      ],
    ),
  );
}

方法3: flutter_keyboard_visibility 三方库

  • 16、运行release模式
    flutter run --release

三、其他代码无关

  • 1、等待异常
Waiting for another flutter command to release the startup lock...

有时候会出现如上等待其他指令执行,但是久久不能好,这个时候关闭IDE也是无效的,具体解决办法可以找到flutter路径,删除路径下的lockfile文件,删除之后再运行,我的文件地址为/Users/xxxxxx/app/flutter/bin/cache/lockfile

  • 2、判断运行模式,debug Or release
    有时候我们希望代码只在debug模式下运行,例如打印信息等,这个时候我们就需要判断一下当前的运行模式了,然后对各种情况做处理,
    具体的用 assert和编译常数kReleaseModel判断,参考
    Flutter 编译模式debug和release判断

  • 3、Xcode运行空白报错 Could not create Dart VM instance.

2019-09-23 16:50:13.088675+0800 Runner[1474:642130] [VERBOSE-2:dart_vm_data.cc(19)] VM snapshot invalid and could not be inferred from settings.
2019-09-23 16:50:13.089348+0800 Runner[1474:642130] [VERBOSE-2:dart_vm.cc(245)] Could not setup VM data to bootstrap the VM from.
2019-09-23 16:50:13.089495+0800 Runner[1474:642130] [VERBOSE-2:dart_vm_lifecycle.cc(89)] Could not create Dart VM instance.
2019-09-23 16:50:13.090369+0800 Runner[1474:642130] [VERBOSE-3:shell.cc(212)] Check failed: vm. Must be able to initialize the VM.
(lldb)
image.png

如上图所示,选择Release模式运行

  • 4、接入极光推送android收不到推送
    可能是权限没有加入,我接入了极光的flutter版,前台是可以收到消息,但是就是不提示,后来增加了权限,解决问题,权限参考Flutter 极光推送android实现

  • 5、Dart Packages 本地存储地址
    /Users/xxx/app/flutter/.pub-cache/hosted

  • 6、创建iOS的oc项目 androd的java项目
    在升级flutter1.9+之后,flutter默认的iOS项目为swift,Android的默认项目为kotlin,执行flutter create 可以看到执行的时候的选项

chh@chhdeMacBook-Pro ~ % flutter create
No option specified for the output directory.
Create a new Flutter project.

If run on a project that already exists, this will repair the project,
recreating any files that are missing.

Usage: flutter create <output directory>
-h, --help                     Print this usage information.
    --[no-]pub                 Whether to run "flutter pub get" after the
                               project has been created.
                               (defaults to on)

    --[no-]offline             When "flutter pub get" is run by the create
                               command, this indicates whether to run it in
                               offline mode or not. In offline mode, it will
                               need to have all dependencies already available
                               in the pub cache to succeed.

    --[no-]with-driver-test    Also add a flutter_driver dependency and generate
                               a sample 'flutter drive' test.

-t, --template=<type>          Specify the type of project to create.

          [app]                (default) Generate a Flutter application.
          [package]            Generate a shareable Flutter project containing
                               modular Dart code.
          [plugin]             Generate a shareable Flutter project containing
                               an API in Dart code with a platform-specific
                               implementation for Android, for iOS code, or for
                               both.

-s, --sample=<id>              Specifies the Flutter code sample to use as the
                               main.dart for an application. Implies
                               --template=app. The value should be the sample ID
                               of the desired sample from the API documentation
                               website (http://docs.flutter.dev). An example can
                               be found at
                               https://master-api.flutter.dev/flutter/widgets/Si
                               ngleChildScrollView-class.html

    --list-samples=<path>      Specifies a JSON output file for a listing of
                               Flutter code samples that can created with
                               --sample.

    --[no-]overwrite           When performing operations, overwrite existing
                               files.

    --description              The description to use for your new Flutter
                               project. This string ends up in the pubspec.yaml
                               file.
                               (defaults to "A new Flutter project.")

    --org                      The organization responsible for your new Flutter
                               project, in reverse domain name notation. This
                               string is used in Java package names and as
                               prefix in the iOS bundle identifier.
                               (defaults to "com.example")

    --project-name             The project name for this new Flutter project.
                               This must be a valid dart package name.

-i, --ios-language             [objc, swift (default)]
-a, --android-language         [java, kotlin (default)]
    --[no-]androidx            Generate a project using the AndroidX support
                               libraries

Run "flutter help" to see global options.
创建选项

根据图片提示知道执行下面可以指定语言,使用下面的命令就可以创建oc和java项目了
flutter create -i objc -a java flutter_demo

  • 7、Android Studio 代码格式化
    option+command+L

  • 8、项目点击run运行报错

// 错误信息
[ERROR:flutter/shell/gpu/gpu_surface_gl.cc(64)] Failed to setup Skia Gr context.

解决方案:
1、指令运行

// 终端运行下列命令运行
flutter run --enable-software-rendering

2、配置信息

AndroidStudio->Run->Edit Configurations->Additional arguments 
输入 --enable-software-rendering
diff: /Users/XXXX/XXXX/XXXX/XXXX/XXXX/ios/Pods/Manifest.lock: No such file or directory
error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.

解决方法

1、 close xcode and enter iOS directory
2、rm -rf Runner.xcworkspace
3、pod install
  • 11、flutter 新项目创建后iOS没有Podfile
    1、创建新的flutter项目flutter create xxx
    2、在flutter项目中引入三方包,最好是和原生包有关系的包,例如scan: 1.6.0,在pubspec.yaml中执行get,iOS项目中就会出现了,这个时候执行pod install 即可

  • 12、Android Studio运行Android项目报错

    image.png

    看保存内容好像是Java版本不兼容的问题,这个时候选择File->Project Structure->SDK Location->Gradle Setting->Gradle JDK 选择java17版本

  • 13、Android项目debug正常打包后有些功能无法使用

    image.png

    可以暂时关闭release模式下的混淆和资源压缩,当然这样包会增加一些

 minifyEnabled false //关闭混淆
 shrinkResources false //关闭压缩资源

五、工具

常用指令
fvm global 3.7.0 进行全局的版本切换-后面是版本号
fvm use 3.7.0 要设置单独项目使用某一个版本运行,首先进入到项目的目录下,使用一下命令,设置使用的版本。

可以在AndroidStudio中安装这个插件,pubspec.yaml中添加图片路径后,点击执行Build->Generate Flutter Assets(如下图)会自动在项目文件生成lib->generated->assets.dart文件,这里面图片名字是个常量,可以直接使用


image.png
Image.asset(Assets.loginIconSelected, width: 20, height: 20)

四、外文链接

有很多文章也进行了很棒的总结,这里贴出地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容