1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#https://github.com/flutterchina/dio/blob/master/README-ZH.md

dio: ^3.0.9 #网络请求框架

cookie_jar: ^1.0.1

dio_cookie_manager: ^1.0.0

path_provider: ^1.6.7

event_bus: ^1.1.1

#https://pub.flutter-io.cn/packages/connectivity

connectivity: ^0.4.8+2 #链接网络状态监测

fluttertoast: ^4.0.1

shared_preferences: ^0.5.6+3

pull_to_refresh: ^1.5.7

modal_progress_hud: ^0.1.3

webview_flutter: ^0.3.20+2

1. 封装Text,只向外提供常用的属性,根据需求适当需改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static Widget text(String data, {
double textScaleFactor = 1.0,
Color color = Colors.black,
double fontSize = 16,
FontWeight fontWeight = FontWeight.normal,
FontStyle fontStyle = FontStyle.normal
}){
return Text(data,
textScaleFactor: textScaleFactor,
style: TextStyle(
color: color,
fontSize: fontSize,
fontWeight: fontWeight,
fontStyle: fontStyle,
),);
}

2. 检测当前主题

1
2
3
4
static bool isDark(BuildContext context){
final Brightness brightnessValue = Theme.of(context).brightness;
return brightnessValue == Brightness.dark;
}

3. 根据主题设置颜色

1
2
3
4
5
6
7
8
9
10
11
//是否是深色模式 true dark主题模式  false light主题模式
static bool isDark(context){
return Theme.of(context).brightness == Brightness.dark;
}
static Color dyColor(BuildContext context,darkColor,lightColor) => isDark(context) ? darkColor : lightColor;
//背景颜色
static Color dyBgColor(BuildContext context) => isDark(context) ? MColor.darkBgColor : Colors.white;
//字体颜色
static Color dyFontColor(BuildContext context) => isDark(context) ? MColor.textColor : MColor.black;
static Color dyRedColor(BuildContext context) => isDark(context) ? Colors.red[400] : Colors.red;
static Color dyBlueColor(BuildContext context) => isDark(context) ? Colors.blue[600] : Colors.blue;

4. 返回一个字体不跟随系统缩放的Widget

1
2
3
4
5
6
7
static noScaleWidget(Widget widget){
return MediaQuery(
data: MediaQueryData
.fromWindow(WidgetsBinding.instance.window)
.copyWith(textScaleFactor: 1),
child: widget);
}

5. 取随机颜色(需要import 'dart:math';

1
2
3
4
5
6
7
static Color getRandomColor() {
var random = new Random();
int r = random.nextInt(255);
int g = random.nextInt(255);
int b = random.nextInt(255);
return Color.fromARGB(255, r, g, b);
}

flutter 升级到 1.12.x android 启动时会黑屏解决办法

  1. 直接用flutter 1.12.x SDK创建的项目

    需要修改AndroidManifest.xml文件,修改如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <application
    android:name="io.flutter.app.FlutterApplication"
    android:label="ifeiyv"
    android:icon="@mipmap/ic_launcher">
    <activity
    android:name=".MainActivity"
    android:launchMode="singleTop"
    android:theme="@style/LaunchTheme"
    android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
    android:hardwareAccelerated="true"
    android:windowSoftInputMode="adjustResize">
    //
    <meta-data
    android:name="io.flutter.embedding.android.SplashScreenDrawable"
    android:resource="@drawable/launch_background"
    />
    <meta-data
    android:name="io.flutter.embedding.android.NormalTheme"
    android:resource="@drawable/launch_background"
    />
    <intent-filter>
    <action android:name="android.intent.action.MAIN"/>
    <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
    </activity>
    <!-- Don't delete the meta-data below.
    This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
    <meta-data
    android:name="flutterEmbedding"
    android:value="2" />
    </application>

    如果存在meta-dataandroid:name="io.flutter.app.android.SplashScreenUntilFirstFrame",删除此meta-data,添加如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    <meta-data
    android:name="io.flutter.embedding.android.SplashScreenDrawable"
    android:resource="@drawable/launch_background"
    />
    <meta-data
    android:name="io.flutter.embedding.android.NormalTheme"
    android:resource="@drawable/launch_background"
    />

    修改launch_background.xml,这个要根据需求添加开屏视图

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?xml version="1.0" encoding="utf-8"?>
    <!-- Modify this file to customize your launch splash screen -->
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
    android:left="0dp"
    android:top="0dp"
    android:bottom="0dp"
    android:right="0dp"
    android:drawable="@drawable/pic_bg" />
    <!-- You can insert your own image assets here -->
    </layer-list>

    <application>标签下添加新的 <meta-data> ,一旦您在AndroidManifest中进行了声明并使用了插件,能够使用使用了新的Android的插件(将插件注册为,FlutterEngine而不是PluginRegistry.Registrar

    1
    2
    3
    < meta-data 
    androidname = “flutterEmbedding”
    androidvalue = “2” />
  1. 如果是低版本创建的项目升级,除了以上修改外还需要修改以下内容:

    1
    2
    3
    4
    5
    //FlutterActivity包路径修改
    //把
    import io.flutter.app.FlutterActivity;
    //修改为
    import io.flutter.embedding.android.FlutterActivity;

以前

1
2
3
4
5
6
7
8
9
10
11
12
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}

// ...some amount of custom code for your app is here.
}

现在

1
2
3
4
5
6
7
8
9
10
11
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
}
}
  1. 这里仅有修复黑屏的问题,更多升级详细信息请前往 官方升级指南 -》 前往

使用AutomaticKeepAliveClientMixin 警告⚠️ This method overrides a method annotated as @mustCallSuper in 'AutomaticKeepAliveClientMixin', but doesn't invoke the overridden method.

该方法将覆盖在’AutomaticKeepAliveClientMixin’中标注为@mustCallSuper的方法,但不会调用覆盖的方法。

解决方案:

  1. 检查 添加AutomaticKeepAliveClientMixin

  2. 添加

    1
    2
    @override <br>
    bool get wantKeepAlive => true;
  3. build方法中记得调用父类方法super.build(context);

    1
    2
    3
    4
    Widget build(BuildContext context) {
    super.build(context);
    return Container();
    }

解决键盘弹起遮挡问题

  • Scaffold的resizeToAvoidBottomPadding属性(v1.1.9之后已废弃)
  • resizeToAvoidBottomInset:为true键盘弹起输入框会自动上移,为false不移动,如果输入框靠下,有可能被遮挡住。默认为true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/// This flag is deprecated, please use [resizeToAvoidBottomInset]
/// instead.
///
/// Originally the name referred [MediaQueryData.padding]. Now it refers
/// [MediaQueryData.viewInsets], so using [resizeToAvoidBottomInset]
/// should be clearer to readers.
@Deprecated(
'Use resizeToAvoidBottomInset to specify if the body should resize when the keyboard appears. '
'This feature was deprecated after v1.1.9.'
)
final bool resizeToAvoidBottomPadding;

/// If true the [body] and the scaffold's floating widgets should size
/// themselves to avoid the onscreen keyboard whose height is defined by the
/// ambient [MediaQuery]'s [MediaQueryData.viewInsets] `bottom` property.
///
/// For example, if there is an onscreen keyboard displayed above the
/// scaffold, the body can be resized to avoid overlapping the keyboard, which
/// prevents widgets inside the body from being obscured by the keyboard.
///
/// Defaults to true.
final bool resizeToAvoidBottomInset;
  • 如果以上配置报错(键盘弹起,布局溢出)界面布局可以用滚动容器承载。eg:把当前界面放到SingleChildScrollView上

创建flutter项目命令

进入到要创建项目的位置,在当前目录下创建项目名称为mydemo的项目

  • 基本默认创建 flutter create mydemo

  • 创建一个 iOS 基于 Swift ,Android基于Java的flutter项目

    1
    flutter create  --ios-language swift --android-language kotlin mydemo
  • 创建一个 iOS 基于 Swift ,Android基于kotlin的flutter项目

    1
    flutter create  --ios-language swift --android-language kotlin mydemo
  • 创建一个 iOS 基于 OC ,Android基于kotlin的flutter项目

    1
    flutter create  --ios-language objc --android-language kotlin mydemo
  • 创建一个 iOS 基于 OC ,Android基于java的flutter项目

    1
    flutter create  --ios-language objc --android-language java mydemo
    • VSCode 配置创建

      配置路径 code>preferences>Settings — User > Extensions > Dart & Flutter

flutter

flutter

flutter 字符串多余n行折叠,点击展开

  • 获取TextPainter
1
2
3
4
5
6
7
8
TextPainter fetchTextPainter(String text, TextStyle style, int maxLine,
double minWidth, double maxWidth) {
return TextPainter(
maxLines: maxLine,
text: TextSpan(text: text, style: style),
textDirection: TextDirection.ltr)
..layout(maxWidth: maxWidth, minWidth: minWidth);
}
  • 判断是否超过n行,需要截断文本
1
2
3
4
5
6
7
8
9
10
11
bool isExpansion(String text, TextStyle style, int maxLine, double minWidth,
double maxWidth) {
TextPainter _textPainter =
fetchTextPainter(text, style, maxLine, minWidth, maxWidth);
if (_textPainter.didExceedMaxLines) {
//这里判断 文本是否截断
return true;
} else {
return false;
}
}
  • 超过n行,需要展开。展开箭头放在文本右下角,移除两个字符(根据需求)用来放展开箭头
1
2
3
4
5
6
7
8
9
10
String expansionString(String text, TextStyle style, int maxLine,
double minWidth, double maxWidth) {
TextPainter _textPainter =
fetchTextPainter(text, style, maxLine, minWidth, maxWidth);
var end = _textPainter
.getPositionForOffset(
Offset(_textPainter.size.width, _textPainter.size.height))
.offset;
return text.substring(0, end - 2) + "...";
}

shared_preferences库

shared_preferences库同时支持Android和ios平台,存键值对信息,进行数据本地持久化存储。

引用方法

  1. 在pubspec.yaml文件中添加依赖

    shared_preferences: ^0.5.3+4->查看最新版本

  2. 执行$ flutter packages get命令 下载插件

  3. 在使用的文件中导入:

    import 'package:shared_preferences/shared_preferences.dart';

使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();

sharedPreferences.setString("name", "hehe");
sharedPreferences.setInt("age", 18);
sharedPreferences.setDouble("height", 1.72);
sharedPreferences.setBool("sex", true);//true 表示男 false表示女
sharedPreferences.setStringList("like", ["MV","Music","Pic"]);

print("=========get****("key")取出存储的数据==============");
print("姓名:"+ sharedPreferences.getString("name"));
print("年龄:" + sharedPreferences.getInt("age").toString());
print("身高:"+ sharedPreferences.getDouble("height").toString());
print("性别:"+ ((sharedPreferences.getBool("sex") == true) ? "男":"女"));
print("爱好:"+ sharedPreferences.getStringList("like").toString());

print("========getKeys()取出存储的所有key值=============");
print(sharedPreferences.getKeys());

print("========set***("key")给已经存在的key重新赋值=============");
print("姓名:"+ sharedPreferences.getString("name"));
sharedPreferences.setString("name", "feiyv");
print("姓名:"+ sharedPreferences.getString("name"));

print("========containsKey("key")判断存储的是否有某个Key值=============");
print("name是否存在:" + sharedPreferences.containsKey("name"));
print("rename是否存在:" + sharedPreferences.containsKey("rename"));

print("========remove("key")删除单个Key数据=============");
print("年龄:" + sharedPreferences.getInt("age").toString());
sharedPreferences.remove("age");
print("年龄:" + sharedPreferences.getInt("age").toString());

print("========clear清除所有数据=============");
sharedPreferences.clear();
print("sharedPreferences.clear();");
print("name是否存在:" + sharedPreferences.containsKey("name").toString());
print("所有的key值:"+ sharedPreferences.getKeys().toString());

打印数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

flutter: =========get****("key")取出存储的数据==============
flutter: 姓名:hehe
flutter: 年龄:18
flutter: 身高:1.72
flutter: 性别:男
flutter: 爱好:[MV, Music, Pic]

flutter: ========getKeys()取出存储的所有key值=============
flutter: {name, age, height, sex, like}

flutter: ========set****("key")给已经存在的key重新赋值=============
flutter: 姓名:hehe
flutter: 姓名:feiyv

flutter: ========containsKey("key")判断存储的是否有某个Key值=============
flutter: name是否存在:true
flutter: rename是否存在:false

flutter: ========remove("key")删除单个Key数据=============
flutter: 年龄:18
flutter: 年龄:null

flutter: ========clear清除所有数据=============
flutter: sharedPreferences.clear();
flutter: name是否存在:false
flutter: 所有的key值:{}

PathProvider 插件

PathProvider 访问设备文件系统上的常用位置。

使用方法:

  1. 在pubspec.yaml文件中添加 PathProvider 插件

    path_provider: ^*.*.*查看最新版本

  2. 在文件中导入:

    import 'package:path_provider/path_provider.dart';

  1. DocumentsDirectory

    文档目录,用于存储只有自己可以访问的文件。只有当应用程序被卸载时,系统才会清除该目录。在iOS上,这对应于NSDocumentDirectory。在Android上,这是AppData目录。

    String docDir = (await getApplicationDocumentsDirectory()).path;

  2. TemporaryDirectory

    系统可随时清除的临时目录(缓存)。在iOS上,这对应于NSTemporaryDirectory() 返回的值。在Android上,这是getCacheDir()返回的目录。

    String tempDir = (await getTemporaryDirectory()).path;

  3. SupportDirectory

    在iOS上,它使用NSApplicationSupportDirectory 来获取目录。在Android上,这是getFilesDir返回的目录。

    String supportDir = (await getApplicationSupportDirectory()).path;

  4. ExternalStorageDirectory

    获取存储卡路径,仅在Android上中有效,iOS系统无此方法,可以通过Platform.isIOS来判断当前系统是否是iOS系统

    String extStorageDir = (await getExternalStorageDirectory()).path;

PathProvider->GitHub

PathProvider->Pub