Flutter采用GPU渲染技术,性能更高,其编写的应用可以达到120fps(每秒传输帧数),完全可以胜任游戏的制作。RN的性能只能达到60fps。
Flutter丰富的插件
安装jdk、配置jdk环境变量
下载Flutter SDK、flutter环境变量配置(可以在终端直接使用flutter命令)
开发工具:Android Studio 或 VS Code
flutter sdk下flutter_console.bat
双击运行并启动flutter命令行。就可以在Flutter命令行运行flutter命令了。
在终端中输入 flutter doctor
进行检测
出现X Android SDK is missing command line tools;...
需要安装Android Studio
AS(Android Studio)配置Flutter插件:File–Settings–Plugin搜索Flutter,点击安装,重启
再打开终端,输入flutter doctor --android-licenses
,安装Android证书
tool
-AVD Manager
选项。Create Virtual Device.....
,如果你一个虚拟机也没建过,这个选项在对话框的中间(我一定跟我的图一样)。Nexus 5x
。(如果你屏幕小,就选择一个小屏幕的虚拟机)Android 9.0
系统,选择好后,又是一个漫长的等待过程。若是直接用真机的话可以不用安装AVD虚拟机
在终端运行flutter doctor
,查看输出是否有问题
或在VS Code的命令面板找到Flutter:Run Flutter Doctor
,执行
VSCode命令面板(cmd+shift+p)支持搜索,输入flutter搜索我们需要的命令
flutter create
Flutter :New Project
执行在VSCode的终端输入flutter run
可以在命令面板送找到
Debug:Select and Start Debugging
执行->选择添加配置->选择Dart&Flutter
,这样就不用每次都选调试环境了。 也可以在调试界面 选择小齿轮 选择Dart&Flutter
在界面右下角可以选择设备
或者命令面板 找到Flutter: Select Devices
在运行flutter的时候打开命令面板输入 Flutter:Toggle
即可看到熟悉的命令
命令面板 Dart: Open Observatory
很多时候VSCode开发体验都蛮好的,但是调试控制台真的难用,还不支持搜索。 不过我们可以设置flutter log输出文件,用其他软件来看log。
在用户设置中搜索 flutter run log 中设置
用其他软件打开这个文件 比如自带的控制台open -a Console .vscode/run.log
Quick fix
或者快速修复
(没错支持中文输入=。=)Sort Members
Sort Members
的快捷键Organize Imports
Fotmat Document
转到定义
Find All References
Flutter扩展包含了一些常用的代码片段
stless
StatelessWidgetstfull
StatefulWidgetstanim
StatefulWidget with AnimationController我们也可以增加自己自定义的代码片段
Configure User Snippets
/ 首选项:配置用户代码片段dart.json
这是我们写的代码片段可做参考
keystore
VSCode
终端:keytool -genkey -v -keystore ~/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key
其中
~/key.jks
要改成我们自己的盘,比如e:\key.jks
flutter doctor -v
这个命令可以找到
keytool.exe
位置
key.properties
在android根目录下新建key.properties
,内容:
storePassword=123123
keyPassword=123123
keyAlias=key
storeFile=E:/key.jks
app/build.gradle
下配置这个签名文件在android{
这行前面加上
def keystorePropertiesFile = rootProject.file("key.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
把如下代码进行替换
buildTypes {
release {
signingConfig signingConfigs.debug
}
}
替换成的代码:
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
直接在终端中输入:
flutter build apk
这时候就打包成功了,剩下的安装过程我就省略
Flutter:包含Flutter的引擎、框架、Widget、工具和Dart SDK
Material 是一种标准的移动端和 web 端的视觉设计语言。
main 函数使用了=>
符号,这是 Dart 中单行函数或方法的简写
MyApp
该应用程序继承了 StatelessWidget
,这将会使应用本身也成为一个 Widget
。在 Flutter
中,大多数东西都是 widget
,包括对齐alignment
、填充padding
、布局layout
main 调用的 MyApp
中要有 MaterialApp
,否则会报错。
在
MaterialApp
中可以设置主题,控制应用程序的外观和风格在
MaterialApp
中至少要创建一个home
或routes
或onGenerateRoute
或builder
Scaffold
是Material library
中提供的一个widget
,它提供了默认的导航栏,标题和包含主屏幕 widget树(可以很复杂)的 body 属性。
StatefulWidget
有状态的部件
持有的状态可能在 Widget
生命周期中发生变化,实现一个 stateful widget 至少需要两个类:StatefulWidget
类、State
类。StatefulWidget
类本身是不变的,但是 State
类在 widget
生命周期中始终存在
widget
主要工作是提供一个build()
方法来描述如何根据其他较低级别的 widget
来显示自己。
ThemeData
属性及描述
属性名 | 类型 | 说明 |
---|---|---|
accentColor |
Color |
前景色(文本、按钮等) |
accentColorBrightness |
Brightness |
``accentColor的亮度。用于确定放置在突出颜色顶部的文本和图标的颜色(例如 FloatingButton`上的图标) |
accentIconTheme |
IconThemeData |
与突出颜色对照的图片主题 |
accentTextTheme |
TextTheme |
与突出颜色对照的文本主题 |
backgroundColor |
Color |
与 primaryColor 对比的颜色(例如:用作进度条的剩余部分) |
bottomAppBarColor |
Color |
BottomAppBar 的默认颜色 |
brightness |
Brightness |
应用程序整体主题的亮度。由按钮等 Widget 使用 |
buttonColor |
Color |
Material 中 RaisedButtons 使用的默认填充色 |
ButtonTheme |
ButtonThemeData |
定义了按钮等空间的默认配置,如 RaisedButton 和 FlatButton |
canvasColor |
Color |
MaterialType.canvas Material 的默认颜色 |
chipTheme |
ChiipThemeData |
用于渲染 Chip 的颜色和样式 |
dialogBackgroundColor |
Color |
Dialog 原色的背景色 |
disabledColor |
Color |
用于 Widget 无效的颜色,包括任何状态。例如禁用复选框 |
dividerColor |
Color |
Dividers 和 PopupMenuDividers 的颜色。也用于 ListTiles 中间和 DataTables 的每行中间 |
errorColor |
Color |
用于输入验证错误的颜色。例如在 TextField 中 |
hashCode |
int |
对象的哈希值 |
highlightColor |
Color |
用于类似墨水喷溅动画或指示菜单被选中的高亮颜色 |
iconTheme |
IconThemeData |
与卡片和画布颜色形成对比的图标主题 |
indicatorColor |
Color |
TabBar 中选项选中的指示器颜色 |
inputDecorationTheme |
InpputDecorationTheme |
InputDecorator 、TextField 和 TextFormField 的默认 InputDecoration 值基于此主题 |
platform |
TargetPlatform |
Widget 需要适配的目标类型 |
primaryColor |
Color |
App 主要部分的背景色(ToolBar 、tabbar 等) |
primaryColorBrightness |
Brightness |
primaryColor 的亮度 |
primaryColorDark |
Color |
primaryColor 的较暗版本 |
primaryColorLight |
Color |
primaryColor 的较亮版本 |
primaryIconTheme |
IconThemeData |
一个与主色对比的图片主题 |
primaryTextTheme |
TextThemeData |
一个与主色对比的文本主题 |
scaffoldBackgroundColor |
Color |
作为 Scaffold 基础的 Material 默认颜色,典型 Material 应用或应用内页面的背景颜色 |
secondaryHeaderColor |
Color |
有选定行时 PaginatedDataTable 标题的颜色 |
selectedRowColor |
Color |
选中行时的高亮颜色 |
sliderTheme |
SliderThemeData |
用于渲染 Slider 的颜色和形状 |
splashColor |
Color |
墨水喷溅的颜色 |
splashFactory |
InteractiveFeatureFactory |
定义 InkWall 和 InkResponse 生成的墨水喷溅的外观 |
textSelectionColor |
Color |
文本字段中选中文本的颜色。例如 TextField |
textSelectionHandleColor |
Color |
用于调整当前文本的哪个部分的句柄颜色 |
textTheme |
TextTheme |
与卡片和画布对比的文本颜色 |
toggleableActiveColor |
Color |
用于突出显示切换 Widget (如 Switch 、Radio 和 Checkbox )的活动状态的颜色 |
unselectedWidgetColor |
Color |
用于 Widget 处于非活动(但已启用)状态的颜色。例如,未选中的复选框。通常与 accentColor 形成对比 |
runtimeType |
Type |
表示对象的运行时类型 |
局部主题:创建特有的主题数据或扩展父主题。
创建特有的主题数据
new Theme(
//创建一个特有的主题数据
data:new ThemeData(
accentColor: Colors.yellow,
),
child: new FloatingActionButton(
onPressed:(){},
child:new Icon(Icons.add),
),
);
扩展父主题(无须覆盖所有的主题属性,用 copyWith)
new Theme(
//覆盖 accentColor 为 Colors.yellow
data: Theme.of(context).copyWith(accentColor: Colors.yellow),
child: new FloatingActionButton(
onPressed: null,
child: new Icon(Icons.add),
),
);
Theme.of(context)
可通过上下文获取主题,查找最近的主题,若找不到就会找整个应用的主题。
第三方库如:网络请求(http)、自定义导航/路由处理(fluro)、集成设备 API(如 url_launcher&battery)以及第三方平台SDK(如 Firebase)等
关键字(56个):abstract、do、import、super、as、dynamic、in、switch、assert、else、interface、sync、enum、implement、is、this、async、export、library、throw、await、external、mixin、true、break、extends、new、try、case、factory、null、typedef、catch、false、operator、var、class、final、part、void、const、finally、rethrow、while、continue、for、return、with、covariant、get、set、yield*、default、if、static、deferred
Dart 语言常用库(使用频率最高 dart:core
、dart:html
、dart:io
)
包名 | 描述 |
---|---|
dart:async |
异步编程支持,提供 Future 和 Stream 类 |
dart:collection |
对 dart:core 提供更多的集合支持 |
dart:convert |
不同类型(JSON,UTF-8)间的字符编码、解码支持 |
dart:core |
Dart 语言内建的类型、对象以及 dart 语言核心的功能 |
dart:html |
网页开发用到的库 |
dart:io |
文件读写 I/O 相关操作的库 |
dart:math |
数字常量及函数,提供随机数算法 |
dart:svg |
事件和动画的矢量图像支持 |
final和const的区别
final wordPair = WordPair.random();
final表明这个变量不能再发生更改,但初始值在编译期是不确定的,在运行时赋值后就不能再更改了。
const city = "烟台";
const定义时需要是个明确的值,在编译时就知道值了。
Dart中单行函数或方法的简写
Dart中表示 if null
//如果b为null则a=hello,否则a=b
String a = b ?? 'hello'
Flutter中大多数是widget,包含对齐(alignment)、填充(padding)和布局(layout)
widget的主要工作是提供一个build()方法来描述如何根据其他较低级别的widget来显示自己Stateless widgets
是不可变的, 这意味着它们的属性不能改变 - 所有的值都是最终(final)的.Stateful widgets
持有的状态可能在widget生命周期中发生变化. 实现一个 stateful widget 至少需要两个类:
StatefulWidget
类。State
类。 StatefulWidget
类本身是不变的,但是 State类在widget生命周期中始终存在。pubspec文件管理Flutter应用程序的assets(资源,如图片、package等)
一个纯 Dart实现的 SDK,类似于 React在 JavaScript中的作用。它实现了一套基础库, 用于处理动画、绘图和手势。并且基于绘图封装了一套 UI组件库,然后根据 Material 和Cupertino两种视觉风格区分开来。这个纯 Dart实现的 SDK被封装为了一个叫作 dart:ui
的 Dart库。我们在使用 Flutter写 App的时候,直接导入这个库即可使用组件等功能。
一个纯 C++实现的 SDK,其中囊括了 Skia
引擎、Dart运行时、文字排版引擎等。不过说白了,它就是 Dart的一个运行时,它可以以JIT
、JIT Snapshot
或者 AOT
的模式运行 Dart
代码。在代码调用 dart:ui
库时,提供 dart:ui
库中 Native Binding
实现。 不过别忘了,这个运行时还控制着 VSync
信号的传递、GPU数据的填充等,并且还负责把客户端的事件传递到运行时中的代码。
Dio
是Flutter下热门的网络请求框架之一
void main()=>runApp(MyApp());
其中 main()是主函数,是程序的入口
flutter 1.0 后就可以省略 new
要调用其他dart 文件的 class 需要在调用的类中加 import ‘文件路径’
;
Scaffold是页面脚手架
list..add(widget 实例)..add(widget 实例);
可以使用..
来给集合连续添加元素
StatefulWidget
要调用 setState(){}
才可以更新内容
BottomNavigationBarItem
是底部导航栏
FloatingActionButton
是浮动的按钮
Navigator
页面跳转 push
,跳转后返回的回调 pop
页面跳转加动画,派生出 PageRouteBuilder
在其构造方法中加动画
毛玻璃效果:写个约束盒子组件ConstrainedBox
(放图),再写个可裁切的矩形 ClipRect
覆盖在上方(在背景过滤器BackdropFilter
中加半透明、宽高、装饰器BoxDeration
等)
上部的导航栏:在 AppBar
中添加多个 Tab 组件
页面状态切换后保持住:派生出 PageState
加 with AutomaticKeepAliveClientMixin
,重写 bool get wantKeepAlive=>true;
搜索条:onpress
中加 showSearch(context:context, delegate:派生 SearchDelegate());会开启一个新页面
派生
SearchDelegate
中buildActions
右侧icon
、buildLeading
左侧icon
、buildResults
搜索结果、buildSuggestions
搜索建议
Wrap流式布局
展开闭合 ExpansionTile
、列表带展开闭合ExpansionPanelList
画曲线:ClipPath
内 clipper:派生CustomClipper
,贝塞尔曲线要确定4 个范围点+控制点+结束点。可以画多条曲线
闪屏动画:派生 State,自定义 AnimationController
用来控制时长、动画播放。Animation 用来设置动画并加 status 监听,结束就跳转
右滑返回:页面导入 cupertino.dart
而不是 material.dart
,用 CupertinoPageScaffold
脚手架+CupertinoButton
+CupertinoPageRoute
轻量级提示:Tooltip
(长按的时候会有提示)
拖拽效果:自定义个DraggableWidget extends StatefulWidget
Positioned
页面脚手架中 Draggable
(feedback 拖动控件的时候子元素的样式、onDraggableCanceled
松手的时候)。外面调用这个自定义的 DraggableWidget
传不同的值即可
在(SatelessWidget)MyApp的build的return中 添加 //去除右上角的debug字样 debugShowCheckedModeBanner: false,
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context){
return new MaterialApp(
...
//去除右上角的debug字样
debugShowCheckdModeBanner: false,
...
);
}
}
常用的提示弹窗(带标题、提示内容、取消按钮、确认按钮)
showDialog(
context: context,
builder: (context) {
return AlertDialog(
...
);
}
);
关闭对话框需要使用
Navigator.of(context, rootNavigator: true).pop(result)
而不是
Navigator.pop(context, result)
ios风格的提示弹窗(带标题、提示内容、取消按钮、确认按钮)
showCupertinoDialog(
context: context,
builder: (context) {
return CupertinoAlertDialog(
...
);
});
builder
通常返回 CupertinoDialog
或者 CupertinoAlertDialog
自定义弹窗
showGeneralDialog(
context: context,
barrierDismissible:true,
barrierLabel: '',
transitionDuration: Duration(milliseconds: 200),
pageBuilder: (BuildContext context, Animation animation,
Animation secondaryAnimation) {
return Center(
child: Container(
height: 300,
width: 250,
color: Colors.lightGreenAccent,
),
);
});
barrierDismissible
:是否可以点击背景关闭。
barrierColor
:背景颜色
transitionDuration
:动画时长,
transitionBuilder
是构建进出动画,默认动画是渐隐渐显,构建缩放动画代码如下:
showGeneralDialog(
transitionBuilder: (BuildContext context, Animation animation,
Animation secondaryAnimation, Widget child) {
return ScaleTransition(scale: animation, child: child);
},
...
)
AboutDialog用于描述当前App信息,底部提供2个按钮:查看许可按钮和关闭按钮。AboutDialog需要和showAboutDialog配合使用,用法如下:
showAboutDialog(
context: context,
applicationIcon: Image.asset(
'images/bird.png',
height: 100,
width: 100,
),
applicationName: '应用程序',
applicationVersion: '1.0.0',
applicationLegalese: 'copyright 老孟,一枚有态度的程序员',
children: [
Container(
height: 30,
color: Colors.red,
),
Container(
height: 30,
color: Colors.blue,
),
Container(
height: 30,
color: Colors.green,
)
],
);
属性说明如下:
applicationIcon
:应用程序的图标。applicationName
:应用程序名称。applicationVersion
:应用程序版本。applicationLegalese
:著作权(copyright)的提示。children
:位置如上图的红蓝绿色的位置。所有的属性都需要手动设置,不是自动获取的。
下面的2个按钮根据应用程序支持的语言显示相应的语言,比如显示中文方法如下:
pubspec.yaml
中配置支持国际化:dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
MaterialApp(
title: 'Flutter Demo',
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('zh', 'CH'),
const Locale('en', 'US'),
],
locale: Locale('zh'),
...
)复制代码
此控件基本不会用到,浏览一下即可。
LicensePage用于描述当前App许可信息,LicensePage需要和showLicensePage配合使用,用法如下:
showLicensePage(
context: context,
applicationIcon: Image.asset(
'images/bird.png',
height: 100,
width: 100,
),
applicationName: '应用程序',
applicationVersion: '1.0.0',
applicationLegalese: 'copyright 老孟,一枚有态度的程序员',
);
页面中下面的英文我们是无法更改的
在最近的Scaffold
父组件上展示一个material风格的bottom sheet,位置同Scaffold
组件的bottomSheet
,如果Scaffold
设置了bottomSheet
,调用showBottomSheet抛出异常。
基本用法如下:
showBottomSheet(
context: context,
builder: (context) {
return Container(height: 200, color: Colors.lightBlue);
});
设置其背景颜色、阴影值、形状:
showBottomSheet(
context: context,
backgroundColor: Colors.lightGreenAccent,
elevation:20,
shape: CircleBorder(),
builder: (context) {
return Container(height: 200);
});
从底部弹出,通常和BottomSheet配合使用,用法如下:
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return BottomSheet(...);
});
设置背景、阴影、形状:
showModalBottomSheet(
context: context,
backgroundColor: Colors.lightBlue,
elevation: 10,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
...
)
isDismissible
:是否可以点击背景关闭。
isScrollControlled
参数指定是否使用可拖动的可滚动的组件,如果子组件是ListView或者GridView,此参数应该设置为true,设置为true后,最大高度可以占满全屏。用法如下:
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (BuildContext context) {
return ListView.builder(
itemBuilder: (context, index) {
return ListTile(
title: Text('老孟$index'),
);
},
itemExtent: 50,
itemCount: 50,
);
});复制代码
showCupertinoModalPopup 展示ios的风格弹出框,通常情况下和CupertinoActionSheet配合使用,用法如下:
showCupertinoModalPopup(
context: context,
builder: (BuildContext context) {
return CupertinoActionSheet(
title: Text('提示'),
message: Text('是否要删除当前项?'),
actions: [
CupertinoActionSheetAction(
child: Text('删除'),
onPressed: () {},
isDefaultAction: true,
),
CupertinoActionSheetAction(
child: Text('暂时不删'),
onPressed: () {},
isDestructiveAction: true,
),
],
);
}
);
filter
参数可以对弹出框以外的区域做模糊或者矩阵操作,用法如下:
showCupertinoModalPopup(
context: context,
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
...
)
弹出框以外的区域有毛玻璃的效果。
showMenu弹出一个Menu菜单,用法如下:
showMenu(
context: context,
position: RelativeRect.fill,
items: [
PopupMenuItem(child: Text('语文')),
PopupMenuDivider(),
CheckedPopupMenuItem(
child: Text('数学'),
checked: true,
),
PopupMenuDivider(),
PopupMenuItem(child: Text('英语')),
]);复制代码
position
参数表示弹出的位置
弹出的位置在屏幕的左上角,我们希望弹出的位置在点击按钮的位置,因此需要计算按钮的位置,计算如下:
final RenderBox button = context.findRenderObject();
final RenderBox overlay = Overlay.of(context).context.findRenderObject();
final RelativeRect position = RelativeRect.fromRect(
Rect.fromPoints(
button.localToGlobal(Offset(0, 0), ancestor: overlay),
button.localToGlobal(button.size.bottomRight(Offset.zero),
ancestor: overlay),
),
Offset.zero & overlay.size,
);复制代码
你需要将按钮单独封装为StatefulWidget组件,否则context代表的就不是按钮组件。
showSearch 是直接跳转到搜索页面,用法如下:
showSearch(context: context, delegate: CustomSearchDelegate());
class CustomSearchDelegate extends SearchDelegate{
@override
List buildActions(BuildContext context) {
return null;
}
@override
Widget buildLeading(BuildContext context) {
return null;
}
@override
Widget buildResults(BuildContext context) {
return null;
}
@override
Widget buildSuggestions(BuildContext context) {
return null;
}
}复制代码
使用showSearch,首先需要重写一个SearchDelegate,实现其中的4个方法。
buildLeading
表示构建搜索框前面的控件,一般是一个返回按钮,点击退出,代码如下:
@override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: Icon(Icons.arrow_back,color: Colors.blue,),
onPressed: (){
close(context, '');
},
);
}
buildSuggestions
是用户正在输入时显示的控件,输入框放生变化时回调此方法,通常返回一个ListView,点击其中一项时,将当前项的内容填充到输入框,用法如下:
@override
Widget buildSuggestions(BuildContext context) {
return ListView.separated(
itemBuilder: (context, index) {
return ListTile(
title: Text('老孟 $index'),
onTap: () {
query = '老孟 $index';
},
);
},
separatorBuilder: (context, index) {
return Divider();
},
itemCount: Random().nextInt(5),
);
}
buildActions
输入框后面的控件,一般情况下,输入框不为空,显示一个清空按钮,点击清空输入框:
@override
List buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(
Icons.clear,
),
onPressed: () {
query = '';
},
)
];
}复制代码
buildResults
是构建搜索结果控件,当用户点击软键盘上的“Search”时回调此方法,一般返回ListView,用法如下:
@override
Widget buildResults(BuildContext context) {
return ListView.separated(
itemBuilder: (context, index) {
return Container(
height: 60,
alignment: Alignment.center,
child: Text(
'$index',
style: TextStyle(fontSize: 20),
),
);
},
separatorBuilder: (context, index) {
return Divider();
},
itemCount: 10,
);
}