内部团队学习,以简单直接为主
创建Flutter项目
- Flutter的项目名称
不可以
使用驼峰命名,可以使用下划线区分 - 通过终端命令
cd 指定目录
// 创建Flutter项目
flutter create hello_flutter
- 通过Android Studio创建 通过Android Studio创建
项目文件介绍
工程目录- .dart_tool:记录依赖库的信息
- .idea:Android Studio是基于IDEA开发的
- android:安卓工程
- ios:iOS工程
- lib:放我们编写的Flutter代码,里面默认有一个
main.dart
,它是我们Flutter启动的入口文件
,里面有main函数
- test:UnitTest
- .gitignore:git忽略文件管理
- .metadata:对Flutter版本做了记录,不要改动
- .packages:依赖库生成文件
- hello_flutter.iml:记录配置信息
- pubspec.lock:依赖库生成文件
- pubspec.yaml:设置flutter工程的依赖库
- README.md:项目描述文件
runApp和Widget
// 启动App
void main() => runApp(MyApp());
/// 是Flutter内部提供的一个函数
/// 当我们启动一个Flutter应用程序时就是从调用这个函数开始的
/// 入参是一个Widget
void runApp(Widget app) {
// 省略代码...
}
-
runApp
是Flutter内部提供的一个函数,启动App的入口,这个函数的入参是一个Widget
-
Widget
可以理解为小部件,Flutter中万物皆Widget
- 在我们iOS或者Android开发中,我们的界面有很多种类的划分:应用(Application)、视图控制器(View Controller)、活动(Activity)、View(视图)、Button(按钮)等等,
在Flutter里这些东西都是不同的Widget而已
- 整个应用程序中所看到的内容
几乎都是Widget
,甚至是内边距的设置,我们也需要使用一个叫Padding的Widget
来做
- 在我们iOS或者Android开发中,我们的界面有很多种类的划分:应用(Application)、视图控制器(View Controller)、活动(Activity)、View(视图)、Button(按钮)等等,
- 导入Flutter默认已经给我们提供的
material
库,来使用其中的很多内置Widget
import 'package:flutter/material.dart';
material设计风格
- material是Google公司推行的一套
设计风格
,或者叫设计语言
、设计规范
等; - 里面有非常多的设计规范,
eg:
颜色、文字的排版、响应动画与过度、填充等等,在Flutter中高度集成了material风格的Widget
- 在应用中,可以直接使用这些内置Widget来创建应用
自定义Widget来解决Flutter嵌套过多的问题
- 在Flutter开发中,我们可以继承自
StatelessWidget
或者StatefulWidget
来创建自己的Widget类;-
StatelessWidget
:没有数据改变的Widget,适用于做一些纯展示
工作; -
StatefulWidget
:有数据改变的Widget,适用于有交互
的复杂场景;
-
- 这里我们先使用
StatelessWidget
来自定义Widget- 让自己创建的Widget继承自StatelessWidget;
- StatelessWidget包含一个必须重写的方法:build方法;
class MyStatelessWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return <返回我们的Widget要渲染的Widget,比如一个Text Widget>;
}
}
-
build方法
什么情况下被执行呢?- 当我们的StatelessWidget第一次被插入到Widget树中时(也就是第一次被创建时)
- 当我们的父Widget(parent widget)发生改变时,子Widget也会被重新构建
- 如果我们的Widget依赖InheritedWidget的一些数据,InheritedWidget数据发生改变时
为什么定义到Widget中的数据一定是不可变的,需要使用final来修饰
?
- Flutter就是这么设计的:
一旦Widget中展示的数据发生变化,就重新构建整个Widget
- Flutter中通过
@immutable
关键字来给类添加注解来限定定义到Widget中的数据一定是不可变的,所以成员变量需要使用final来修饰
(这里涉及到Dart的元编程
,后面再说),如下所示:
@immutable
abstract class Widget extends DiagnosticableTree {
// 省略代码...
}
StatelessWidget
- Android Studio的智能提示是
区分大小写
的,如:MaterialApp,如果打materialApp是不能正确进行智能提示的
- StatelessWidget综合示例,
注意看代码注释
/// 导入Flutter提供的Material库,可以使用其中很多内置Widget
import 'package:flutter/material.dart';
/// runApp是Flutter内部提供的一个函数
/// 当我们启动一个Flutter应用程序时就是从调用这个函数开始的
/// 入参是一个Widget
void main() => runApp(MyApp());
/// 自定义Widget来减少嵌套层级
class MyApp extends StatelessWidget {
/// 继承自StatelessWidget必须实现此方法
@override
Widget build(BuildContext context) {
return MaterialApp(
/// 去除debug标记
debugShowCheckedModeBanner: false,
/// 设置主题
theme: ThemeData(primaryColor: Colors.yellowAccent),
/// Scaffold是脚手架
home: Scaffold(
/// 导航栏
appBar: AppBar(
title: Text("火信"),
),
/// body是页面的内容部分
body: HomeView(),
),
);
}
}
/// 自定义Widget - 首页Widget
class HomeView extends StatelessWidget {
@override
Widget build(BuildContext context) {
/// 界面整体边距
return Padding(
/// padding属性用于设置边距大小
padding: const EdgeInsets.all(8.0),
/// 如果这里使用Column会出错
/// 因为在Flutter的布局中,内容是不能超出屏幕范围的,当超出时不会自动变成滚动效果,而是会报下面的错误
///
/// 解决方案:Column换成ListView
/// ListView可以让自己的子Widget变成滚动的效果
child: ListView(
children: <Widget>[
ProductCell("火信1.0", "来火信,聊懂区块链",
"https://bkimg.cdn.bcebos.com/pic/09fa513d269759ee3c61708ebffb43166d22df45?x-bce-process=image/watermark,g_7,image_d2F0ZXIvYmFpa2U4MA==,xp_5,yp_5"),
ProductCell("火信2.0", "来火信,聊懂区块链",
"https://bkimg.cdn.bcebos.com/pic/09fa513d269759ee3c61708ebffb43166d22df45?x-bce-process=image/watermark,g_7,image_d2F0ZXIvYmFpa2U4MA==,xp_5,yp_5"),
ProductCell("火信3.0", "来火信,聊懂区块链",
"https://bkimg.cdn.bcebos.com/pic/09fa513d269759ee3c61708ebffb43166d22df45?x-bce-process=image/watermark,g_7,image_d2F0ZXIvYmFpa2U4MA==,xp_5,yp_5"),
],
),
);
}
}
/// 自定义Widget - 单个Cell Widget
class ProductCell extends StatelessWidget {
final String title;
final String desc;
final String imageUrl;
/// 构造函数
ProductCell(this.title, this.desc, this.imageUrl);
@override
Widget build(BuildContext context) {
/// Container用于设置内边距和边框
return Container(
padding: EdgeInsets.all(20),
/// 通过decoration来设置边框
decoration: BoxDecoration(border: Border.all()),
child: Column(
children: <Widget>[
/// Text文本Widget
Text(title, style: TextStyle(fontSize: 24)),
Text(desc, style: TextStyle(fontSize: 18)),
/// 文字图片的间距
SizedBox(
/// 设置一个height属性,可以增加一些距离
height: 10,
),
/// Image是图片Widget
Image.network(imageUrl)
],
),
);
}
}
- 运行效果 火信
StatefulWidget
- Flutter将
StatefulWidget
设计成了两个类StatefulWidget
和State
- 一个类继承自
StatefulWidget
,作为Widget树的一部分
- 一个类继承自
State
,用于记录变化的状态,并且根据状态的变化,构建出新的Widget
- 之所以这样设计,是因为在Flutter中,只要
数据改变了
就需要rebuild Widget
- StatefulWidget的综合示例,
注意看代码注释
/// 导入Flutter提供的Material库,可以使用其中很多内置Widget
import 'package:flutter/material.dart';
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
/// MaterialApp提示区分大小写
return MaterialApp(
/// 去除debug标记
// debugShowCheckedModeBanner: false,
/// 设置主题
theme: ThemeData(primaryColor: Colors.yellowAccent),
home: HomeWidget(),
);
}
}
class HomeWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
/// Scaffold是脚手架,区分大小写
return Scaffold(
/// Scaffold提示区分大小写
appBar: AppBar(
title: Text("HuobiChat"),
),
body: HomeView("你好,火信!"),
);
}
}
/// Widget是不加_: 暴露给外部使用
class HomeView extends StatefulWidget {
final String message;
HomeView(this.message);
@override
State<StatefulWidget> createState() {
return _HomeViewState();
}
}
/// 为什么Flutter在设计的时候StatefulWidget的build方法放在State中
/// 1.build出来的Widget是需要依赖State中的变量(状态/数据)
/// 2.在Flutter的运行过程中:
/// Widget是不断的销毁和创建的
/// 当我们自己的状态发生改变时, 并不希望重新状态一个新的State
/// State是加_: 这样状态这个类只是给StatefulWidget内部使用
class _HomeViewState extends State<HomeView> {
int _counter = 0;
@override
Widget build(BuildContext context) {
/// 居中Widget
return Center(
/// Column小部件:当有垂直方向布局时,就使用它;
child: Column(
/// 居中
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_getButtons(),
Text(
"当前计数:$_counter",
style: TextStyle(fontSize: 25),
),
Text("传递的信息:${widget.message}")
],
),
);
}
Widget _getButtons() {
/// Row小部件:当时水平方向布局时,就使用它
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
/// RaiseButton小部件:可以创建一个按钮,并且其中有一个onPressed属性是传入一个回调函数,当点击按钮时回调
RaisedButton(
child: Text(
"+",
style: TextStyle(fontSize: 20, color: Colors.white),
),
color: Colors.pink,
onPressed: () {
/// 手动调用setState方法,会根据最新的状态(数据)来重新调用build方法,构建对应的Widgets
setState(() {
_counter++;
});
},
),
RaisedButton(
child: Text(
"-",
style: TextStyle(fontSize: 20, color: Colors.white),
),
color: Colors.purple,
onPressed: () {
setState(() {
_counter--;
});
}),
],
);
}
}
-
运行效果
HuobiChat
生命周期
StatelessWidget生命周期
- 代码演示
import 'package:flutter/material.dart';
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeWidget(),
);
}
}
class HomeWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return HomeView("1111");
}
}
// StatelessWidget的生命周期
class HomeView extends StatelessWidget {
HomeView() {
print("1---构造函数被调用");
}
@override
Widget build(BuildContext context) {
print("2---调用build方法");
return Text("111");
}
}
- 打印结果
flutter: 1---构造函数被调用
flutter: 2---调用build方法
StatefulWidget生命周期
- 生命周期图示 StatefulWidget生命周期
-
代码演示
import 'package:flutter/material.dart';
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeWidget(),
);
}
}
class HomeWidget extends StatefulWidget {
@override
_HomeWidgetState createState() => _HomeWidgetState();
}
class _HomeWidgetState extends State<HomeWidget> {
@override
Widget build(BuildContext context) {
/// Scaffold是脚手架,区分大小写
return Scaffold(
/// Scaffold提示区分大小写
appBar: AppBar(
title: Text("StatefulWidget的生命周期"),
),
body: HomeView(),
floatingActionButton: RaisedButton(
child: Icon(Icons.add),
/// 点击这里会让HomeView重新构建,这时就会触发HomeViewState的didUpdateWidget方法
onPressed: () {
setState(() {
print("rebuid Widget");
});
},
),
);
}
}
// StatefulWidget的生命周期
class HomeView extends StatefulWidget {
HomeView() {
print("1---调用HomeView的constructor方法");
}
@override
_HomeViewState createState() {
print("2---调用HomeView的createState方法");
return _HomeViewState();
}
}
class _HomeViewState extends State<HomeView> {
int _counter = 0;
_HomeViewState() {
print("3---调用_HomeViewState的constructor方法");
}
/// 执行initState,我们通常会在这个方法中执行一些数据初始化的操作,或者也可能会发送网络请求
/// 这个方法是重写父类的方法,必须调用super,因为父类中会进行一些其他操作;
/// initState上有一个注解(annotation):@mustCallSuper
@override
void initState() {
// 调用: 这里是必须调用super
final TextStyle style = TextStyle();
super.initState();
print("4---调用_HomeViewState的initState方法");
}
/// 执行didChangeDependencies方法,这个方法在两种情况下会调用
/// 情况一:调用initState会调用;
/// 情况二:从其他对象中依赖一些数据发生改变时,比如前面我们提到的InheritedWidget(这个后面会讲到);
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("5---调用_HomeViewState的didChangeDependencies方法");
}
/// 执行build方法,来看一下我们当前的Widget需要渲染哪些Widget
@override
Widget build(BuildContext context) {
print("6---调用_HomeViewState的build方法");
return Column(
children: <Widget>[
RaisedButton(
child: Icon(Icons.add),
onPressed: () {
setState(() {
_counter++;
});
},
),
Text("数字:$_counter")
],
);
}
/// 当前的Widget不再使用时,会调用dispose进行销毁
@override
void dispose() {
print("---调用_HomeViewState的dispose方法");
super.dispose();
}
/// 执行didUpdateWidget方法是在当父Widget触发重建(rebuild)时,系统会重新构建Widget时会调用didUpdateWidget方法
@override
void didUpdateWidget(HomeView oldWidget) {
super.didUpdateWidget(oldWidget);
print("---didUpdateWidget");
}
}
- 打印结果
flutter: 1---调用HomeView的constructor方法
flutter: 2---调用HomeView的createState方法
flutter: 3---调用_HomeViewState的constructor方法
flutter: 4---调用_HomeViewState的initState方法
flutter: 5---调用_HomeViewState的didChangeDependencies方法
flutter: 6---调用_HomeViewState的build方法
// 这里是Android Studio里才会打印,VSCode里就不会,具体原因不详
flutter: 1---调用HomeView的constructor方法
flutter: ---didUpdateWidget
flutter: 6---调用_HomeViewState的build方法
- 当点击右下角的加号按钮时,会手动执行
setState
方法后会打印如下结果
flutter: rebuid Widget
flutter: 1---调用HomeView的constructor方法
flutter: ---didUpdateWidget
flutter: 5---调用_HomeViewState的build方法
命令式编程 和 声明式编程
-
命令式编程
:命令式编程非常好理解,就是一步步给计算机命令,告诉它我们想做什么事情; -
声明式编程
:声明式编程通常是描述目标的性质,你应该是什么样的,依赖哪些状态,并且当依赖的状态发生改变时,我们通过某些方式通知目标作出相应; - 目前在
Vue
、React
、包括iOS中的SwiftUI
中以及Flutter
目前都采用了声明式编程
网友评论