一、前言
最近在使用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、文本和图标一行展示
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)
如上图所示,选择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
9、 Flutter SDK版本下载国内镜像地址
https://mirrors.tuna.tsinghua.edu.cn/flutter/flutter_infra/releases/stable/macos/10 、flutter ios编译出现错误:
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项目报错
看保存内容好像是Java版本不兼容的问题,这个时候选择File->Project Structure->SDK Location->Gradle Setting->Gradle JDK 选择java17版本 -
13、Android项目debug正常打包后有些功能无法使用
可以暂时关闭release模式下的混淆和资源压缩,当然这样包会增加一些
minifyEnabled false //关闭混淆
shrinkResources false //关闭压缩资源
五、工具
- 1、flutter多版本管理工具fvm,使用参考Flutter多版本管理工具fvm使用
常用指令
fvm global 3.7.0
进行全局的版本切换-后面是版本号
fvm use 3.7.0
要设置单独项目使用某一个版本运行,首先进入到项目的目录下,使用一下命令,设置使用的版本。
- 2、图片名称生成插件FlutterAssetsGenerator
可以在AndroidStudio中安装这个插件,pubspec.yaml中添加图片路径后,点击执行Build->Generate Flutter Assets(如下图)会自动在项目文件生成lib->generated->assets.dart文件,这里面图片名字是个常量,可以直接使用
Image.asset(Assets.loginIconSelected, width: 20, height: 20)
四、外文链接
有很多文章也进行了很棒的总结,这里贴出地址