1. 热重载
1.1 什么是热重载
- Flutter热重载:在我们调试布局的时候,更改代码,模拟器上可以立即看到代码改动的效果;而不是iOS和Android单独开发需要重新编译,启动,耗时极长。
- 开发效率:Flutter的热重载可帮助开发者快速地进行测试、构建UI、添加功能并更快地修复错误。在iOS和Android模拟器或真机上可以实现毫秒级热重载,并且不会丢失状态。这真的很棒,相信我,如果你是一名原生开发者,体验了Flutter开发流后,很可能就不想重新回去做原生了,毕竟很少有人不吐槽原生开发的编译速度。
1.2 热重载的使用
热重载快捷键:
command + \
或者直接点击热重载按钮,闪电标识:
如果使用终端直接run,热重载直接输入小写的 r,即可;热重启直输入大写的 R,即可
2. 常用快捷键的使用
-
Dart语言命名规则:
文件名:小写 + _ (组合使用,不需要驼峰规则)
类名 :首字母大写 + 驼峰规则 -
格式化: 按照Dart的语法规则将代码格式优化
command + option + l -
function折叠: 将{}之间的方法折叠为{…}或展开
command -
command + -
代码块的使用:
stl (无状态stateless widget)
stf (有状态stateful widget) -
import快捷键: 当使用一个没有import的类时报错,可以使用如下快捷键
option + enter两次 -
切换文件: 在两个.dart文件之间来回切换
command + [
command + ] -
打开一个文件:
command + shift + o -
当一个function里面只有一句执行代码的时候,可以用=>简写,如下:
void main() {
runApp(App(); //只有一句执行代码
}void main() => runApp(App());
3. 简单ListView的搭建
Flutter中的ListView类似于iOS中的UITableView,使用之处和iOS的UITableView类似也有区别;
3.1 Widget控件
- 在Flutter中几乎所有的对象都是一个Widget。与原生开发中“控件”不同的是,Flutter中的Widget的概念更广泛,它不仅可以表示UI元素,也可以表示一些功能性的组件如:用于手势检测的 GestureDetector widget、用于APP主题数据传递的Theme等等,而原生开发中的控件通常只是指UI元素。在后面的内容中,我们在描述UI元素时可能会用到“控件”、“组件”这样的概念,读者心里需要知道他们就是widget,只是在不同场景的不同表述而已。由于Flutter主要就是用于构建用户界面的,所以,在大多数时候,读者可以认为widget就是一个控件,不必纠结于概念。
Widget主要接口:
@immutable
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });
final Key key;
@protected
Element createElement();
@override
String toStringShort() {
return key == null ? '$runtimeType' : '$runtimeType-$key';
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
}
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
}
- Widget类继承自DiagnosticableTree,DiagnosticableTree即“诊断树”,主要作用是提供调试信息。
- Key: 这个key属性类似于React/Vue中的key,主要的作用是决定是否在下一次build时复用旧的
- widget,决定的条件在canUpdate()方法中。
- createElement():正如前文所述“一个Widget可以对应多个Element”;Flutter Framework在构建UI树时,会先调用此方法生成对应节点的Element对象。此方法是Flutter Framework隐式调用的,在我们开发过程中基本不会调用到。
- debugFillProperties(…) 复写父类的方法,主要是设置诊断树的一些特性。
- canUpdate(…)是一个静态方法,它主要用于在Widget树重新build时复用旧的widget,其实具体来说,应该是:是否用新的Widget对象去更新旧UI树上所对应的Element对象的配置;通过其源码我们可以看到,只要newWidget与oldWidget的runtimeType和key同时相等时就会用newWidget去更新Element对象的配置,否则就会创建新的Element。
以上摘自Flutter官方文档Widget概述:Flutter中文网Widget概述
简单来说Widget就相当于iOS中的UIView
3.2 主要代码
代码主要分为3部分:
- main.dat:主界面,导航栏等;
- listview_demo.dart 列表listview对象控件;
- car.dart 单个widget模型(name,imageUrl);
- main.dart中的代码:
//material.dart相当于iOS中的UIKit
import 'package:flutter/material.dart';
import 'package:flutter_001/model/listview_demo.dart';
//Widget 控件 相当于UIView
//Stateful 有状态的控件 Stateless 无状态的控件
//void main (){
//
// runApp(
// App(),
// );
//}
//当一个function里面只有一句执行代码的时候,可以用=>简写
//如下:
void main() => runApp(App());
//runApp相当于iOS中的UIApplication
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: true,
home: Home(),//首页
theme: ThemeData(
primaryColor: Colors.yellow,//导航栏颜色
),
);
}
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
//appBar 导航栏
appBar: AppBar(title: Text(
"ListView",
style: TextStyle(
color: Colors.deepOrange[300],
fontSize: 22,
),
),
),
body: ListViewDemo(), //ListView控件
);
}
}
- 列表listview_demo的代码:
import 'package:flutter/material.dart';
import 'car.dart'; //option +enter两次
class ListViewDemo extends StatelessWidget {
Widget _cellForRow (BuildContext context, int index){
//自定义返回cell的函数
//context:上下文
//index:第几个widget(第几个cell)
return Container(
margin: EdgeInsets.all(10),
color: Colors.white,
child: Column(
children: <Widget>[
Image.network(datas[index].imageUrl),
SizedBox(height:20),
Text(datas[index].name,style:
TextStyle(color:Colors.blue,
fontSize:22,
fontWeight: FontWeight.w800,
fontStyle: FontStyle.values[1],
)),
SizedBox(height: 20),
],
),
);
}
//Column 上下布局
//Row 左右布局
//Stack 层级布局
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: datas.length,
itemBuilder: _cellForRow);
}
// ListView.builder创建listView,需要传入的参数有
// itemCount: 简单理解UITableView中的cell的个数;
// itemBuilder: 简单理解UITableView中的cellforRowsAtIndex;
// Dart中带_的方法为私有方法,外部不能访问,例如_cellForRow;
}
- 模型car.dart文件中的代码:
class Car {
const Car ({ //构造函数
this.name,
this.imageUrl,
});
final String name;
final String imageUrl;
}
//数据源数组:
final List<Car> datas = [
Car(
name: '保时捷918 Spyder',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-7d8be6ebc4c7c95b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '兰博基尼Aventador',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-e3bfd824f30afaac?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '法拉利Enzo',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-a1d64cf5da2d9d99?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: 'Zenvo ST1',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-bf883b46690f93ce?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '迈凯伦F1',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-5a7b5550a19b8342?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '萨林S7',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-2e128d18144ad5b8?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '科尼赛克CCR',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-01ced8f6f95219ec?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '布加迪Chiron',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-7fc8359eb61adac0?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '轩尼诗Venom GT',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-d332bf510d61bbc2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
),
Car(
name: '西贝尔Tuatara',
imageUrl:
'https://upload-images.jianshu.io/upload_images/2990730-3dd9a70b25ae6bc9?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240',
)
];
原文链接:链接