1.下载安装包
2.将压缩包解压,然后把其中的 flutter 目录整个放在你想放置 Flutter SDK 的路径中
勿将 Flutter 安装在需要高权限的文件夹内,例如 C:\Program Files\。
Environment Variables->User Variables->PATH->New加入 flutter\bin 目录的完整路径
配置国内镜像,新增加环境变量
File > Settings > Plugins下载Flutter和Dart插件
配置国内依赖
android/build.gradle替换如下内容
repositories {
//google()
//jcenter()
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/jcenter' }
maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
}
插件版本和gradle版本匹配
查看gradle版本
android/build.gradle
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
android/gradle\wrapper\gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
在pubspec.yaml中保持如下设置,使用更多 Material 的特性
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
View是Android中显示在屏幕上的一切基础;
Widget大致是Flutter中的对应的View。
差异①:
widget 有着不一样的生命周期:它们是不可变的,一旦需要变化则生命周期终止。任何时候 widget 或它们的状态变化时, Flutter 框架都会创建一个新的 widget 树的实例。
Android View 只会绘制一次,除非调用 invalidate 才会重绘。
Flutter 的 widget 很轻量,部分原因在于它们的不可变性。因为它们本身既非视图,也不会直接绘制任何内容,而是 UI 及其底层创建真正视图对象的语义的描述。
差异②:
Android中可以直接更新View;
Flutter中Widget是不可变的,不能直接更新,需要操作Widget的状态。
StatelessWidget用于用户界面的一部分不依赖于除了对象中的配置信息以外的任何东西的场景。如Android中的ImageView,这个图标运行中不会改变,在Flutter中即StatelessWidget。
Text(
'I like Flutter!',
style: TextStyle(fontWeight: FontWeight.bold),
);
StatefulWidget用于用户界面的一部分需要和用户动态交互的部分场景。如根据 HTTP 请求返回的数据或者用户的交互来动态地更新界面,并告诉 Flutter 框架 Widget 的状态 (State) 更新了,以便 Flutter 可以更新这个 Widget。
import 'package:flutter/material.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({
Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
// Default placeholder text.
String textToShow = 'I Like Flutter';
void _updateText() {
setState(() {
// Update the text.
textToShow = 'Flutter is Awesome!';
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Sample App'),
),
body: Center(child: Text(textToShow)),
floatingActionButton: FloatingActionButton(
onPressed: _updateText,
tooltip: 'Update Text',
child: Icon(Icons.update),
),
);
}
}
这里需要着重注意的是,无状态和有状态的 Widget 本质上是行为一致的。它们每一帧都会重建,不同之处在于 StatefulWidget 有一个跨帧存储和恢复状态数据的 State 对象。
如果一个 Widget 会变化(例如由于用户交互),它是有状态的。然而,如果一个 Widget 响应变化,它的父 Widget 只要本身不响应变化,就依然是无状态的。
差异③:
Android中使用XML文件定义布局;
Flutter中使用Widget树定义布局。
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Sample App'),
),
body: Center(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.only(left: 20.0, right: 30.0),
),
onPressed: () {
},
child: Text('Hello'),
),
),
);
}
差异④:
Android中通过调用父 View 的 addChild() 或 removeChild() 方法动态地添加或者删除子 View;
Flutter中由于 Widget 是不可变的,所以没有 addChild() 的直接对应的方法。可以给返回一个 Widget 的父 Widget 传入一个方法,并通过布尔标记值toggle控制子 Widget 的创建。
class _SampleAppPageState extends State<SampleAppPage> {
// Default value for toggle.
bool toggle = true;
void _toggle() {
setState(() {
toggle = !toggle;
});
}
Widget _getToggleChild() {
if (toggle) {
return Text('Toggle One');
} else {
return ElevatedButton(
onPressed: () {
},
child: Text('Toggle Two'),
);
}
}
差异⑤:
Android中既可以通过 XML 文件定义动画,也可以调用 View 对象的 animate() 方法;
Flutter中使用动画库,通过将 Widget 嵌入一个动画 Widget 的方式实现 Widget 的动画效果。
Flutter 通过 Animation 的子类 AnimationController 来暂停、播放、停止以及逆向播放动画。它需要一个 Ticker 在垂直同步 (vsync) 的时候发出信号,并且在运行的时候创建一个介于 0 和 1 之间的线性插值。然后就可以创建一个或多个 Animation,并将它们绑定到控制器上。
下面的例子展示了如何实现一个点击 FloatingActionButton 的时候将一个 Widget 渐变为一个图标的 FadeTransition:
import 'package:flutter/material.dart';
void main() {
runApp(FadeAppTest());
}
class FadeAppTest extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fade Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyFadeTest(title: 'Fade Demo'),
);
}
}
class MyFadeTest extends StatefulWidget {
MyFadeTest({
Key key, this.title}) : super(key: key);
final String title;
@override
_MyFadeTest createState() => _MyFadeTest();
}
class _MyFadeTest extends State<MyFadeTest> with TickerProviderStateMixin {
AnimationController controller;
CurvedAnimation curve;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(milliseconds: 2000),
vsync: this,
);
curve = CurvedAnimation(
parent: controller,
curve: Curves.easeIn,
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: FadeTransition(
opacity: curve,
child: FlutterLogo(
size: 100.0,
),
),
),
floatingActionButton: FloatingActionButton(
tooltip: 'Fade',
onPressed: () {
controller.forward();
},
child: Icon(Icons.brush),
),
);
}
}
差异⑥:
Android中使用 Canvas 和 Drawable 将图片和形状绘制到屏幕上;
Flutter中使用类似于 Canvas 的 API,因为它基于相同的底层渲染引擎 Skia。
Flutter 有两个用画布 (canvas) 进行绘制的类:CustomPaint 和 CustomPainter,后者可以实现自定义的绘制算法。
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: DemoApp()));
class DemoApp extends StatelessWidget {
Widget build(BuildContext context) => Scaffold(body: Signature());
}
class Signature extends StatefulWidget {
SignatureState createState() => SignatureState();
}
class SignatureState extends State<Signature> {
List<Offset> _points = <Offset>[];
Widget build(BuildContext context) {
return GestureDetector(
onPanUpdate: (DragUpdateDetails details) {
setState(() {
RenderBox referenceBox = context.findRenderObject();
Offset localPosition =
referenceBox.globalToLocal(details.globalPosition);
_points = List.from(_points)..add(localPosition);
});
},
onPanEnd: (DragEndDetails details) => _points.add(null),
child: CustomPaint(
painter: SignaturePainter(_points),
size: Size.infinite,
),
);
}
}
class SignaturePainter extends CustomPainter {
SignaturePainter(this.points);
final List<Offset> points;
void paint(Canvas canvas, Size size) {
var paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0;
for (int i = 0; i < points.length - 1; i++) {
if (points[i] != null && points[i + 1] != null)
canvas.drawLine(points[i], points[i + 1], paint);
}
}
bool shouldRepaint(SignaturePainter other) => other.points != points;
}
差异⑦:
Android中通过继承 View 类,或者使用已有的视图类,再覆写或实现可以达到特定效果的方法;
Flutter中通过 组合 更小的 Widget 来创建自定义 Widget(而不是继承它们)。
创建一个在构造器接收标签参数的 CustomButton?你要组合 RaisedButton 和一个标签来创建自定义按钮,而不是继承 RaisedButton:
class CustomButton extends StatelessWidget {
final String label;
CustomButton(this.label);
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
},
child: Text(label),
);
}
}
使用CustomButton:
@override
Widget build(BuildContext context) {
return Center(
child: CustomButton("Hello"),
);
}
差异⑧:
Android中Intent 主要有两个使用场景:在 Activity 之前进行导航,以及组件间通信。 Flutter 却没有 intent 这样的概念,但是你依然可以通过原生集成 (插件) 来启动 intent;
Flutter没有 Activity 和 Fragment 的对应概念。在 Flutter 中需要使用 Nav