项目文件目录结构介绍
注:创建 Flutter 项目名称不要包含特殊字符,不要使用驼峰标识
// TODO
开发中运行一个 Flutter 三种启动方式
Run 冷启动 | 从零开始启动 |
Hot Reload 热重载 | 执行 build 方法 |
Hot Restart 热重启 | 重新运行整个 APP |
先看效果,最终要实现一个复选框点击效果
代码会从简到繁一点一点增加
代码分析
// Dart 程序入口是 main 函数,Flutter 是 Dart 编写的,所以入口也是 main 函数
main(){
/**
* 1. runApp 函数 (Flutter 内部提供的一个函数)
* 当启动一个 Flutter 应用程序时调用 runApp
*/
runApp(
/**
* runApp入参:Widget
*
* 2 Widget(Flutter 中万物皆 Widget)
* 整个应用程序中所看到的内容几乎都是 Widget,甚至是内边距的设置,
* 需要使用一个叫 Padding 的 Widget 来做
*/
);
}
改进页面样式
main() {
runApp(
/**
* 需求:
* 1 居中显示: 需要使用另外一个Widget,Center
* 2 文字大一些: 需要给Text文本设置一些样式
* 在 Text 小部件外层包装了一个 Center 部件,让 Text 作为其 child
*/
Center(
child: Text('Hello Flutter',
textDirection: TextDirection.ltr,
style: TextStyle(
fontSize: 28,
color: Colors.red
)
)
)
);
}
改进页面结构
main() {
runApp(
/**
* 需求:添加导航栏
* 最外层包裹一个 MaterialApp【Material 是 Google 推行的一套设计风格、设计规范】
* title:Android 系统中打开多任务切换窗口时显示的标题;(可以不写)
* home:应用启动时显示的页面,传入了一个 Scaffold
*
* Scaffold:
* 直译:[脚手架],作用:搭建页面的基本结构
* 所以给 MaterialApp 的 home 属性传入了一个 Scaffold 对象,作为启动显示的 Widget
* Scaffold 也有一些属性,如 appBar 和 body
* appBar:用于设计导航栏,传入了一个 title 属性
* body:页面的内容,传入了之前已经创建好的 Center 中包裹的一个 Text 的 Widget
*/
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Title'),
),
body: Center(
child: Text('Hello Flutter',
textDirection: TextDirection.ltr,
style: TextStyle(fontSize: 28, color: Colors.red))
)
),
)
);
}
功能进阶
main() {
runApp(
/**
* 需求:改为一个复选框,复选框右边有说明文案
* Row: 一个水平布局的小部件,将子控件水平排列
*/
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('这是Flutter Title'),
),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Checkbox(
value: true,
onChanged: (value) => print('Checkbox 被点击了')
),
Text(
'同意条款',
style: TextStyle(fontSize: 22),
)
],
)
)
),
)
);
}
代码到此层级嵌套已很多,且Checkbox点击有问题,此处会引出 StatefulWidget / StatelessWidget
代码重构
main() {
runApp(
/**
* Flutter 开发过程中会形成一个 Widget树,层级嵌套属正常
* Flutter 代码缩进,开发中使用 2个空格
*
* 问题:开发这么简单的程序出现这么多层级嵌套,后期应用程序更复杂层级嵌套会非常恐怖
* 解决:可以对代码进行封装,将它们封装到自定义 Widget 中,创建自己的 Widget
*
* 在 Flutter开发中,可以继承 StatelessWidget/StatefulWidget 自定义 Widget 类
* 有状态的Widget: StatefulWidget 在运行过程中有一些状态需要改变程序
* 无状态的Widget: StatelessWidget 内容是确定没有状态的改变,无状态Widget通常仅仅做一些展示
*
* 先使用 StatelessWidget 重构:
* StatelessWidget 通常是一些没有状态(State,也可以理解成data)需要维护的 Widget
* 它们的数据通常是直接写死(放在Widget中的数据,必须被定义为 final)
* 从 parent widget中传入的且一旦传入就不可修改
*/
MyApp());
}
/**
* StatelessWidget 包含一个必须重写的方法:build()
* Flutter 在拿到自定义的 StatelessWidget 时,会执行它的 build 方法
* StatelessWidget 没办法主动去执行 build方法,当使用的数据发生改变时,build方法会被重新执行
*
* build 方法执行:
* 1 当 StatelessWidget 第一次被插入到Widget树中时(第一次被创建时)
* 2 当父 Widget(parent widget)发生改变时,子 Widget会被重新构建
* 3 如果自定义的 Widget 依赖 InheritedWidget 的一些数据,InheritedWidget数据发生改变时
*/
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyHomePage()
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('这是Flutter Title'),
),
body: MyCenterBody());
}
}
// flag:状态
// Stateful不能定义状态 -> 创建一个单独的类,这个类负责维护状态
// Widget 不加下划线_ ,因为需要暴漏给别人使用
class MyCenterBody extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _MyContentBodyState();
}
}
// State加下划线_ ,因为状态只给 Widget 使用
class _MyContentBodyState extends State<MyCenterBody>{
var flag = true;
@override
Widget build(BuildContext context) {
return Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Checkbox(
value: flag,
onChanged: (value){
setState(() {
flag = value!;
});
}
),
Text(
'同意条款',
style: TextStyle(fontSize: 20),
)
],
)
);
}
}
ListView项目
main() => runApp(MyApp());
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomeContent()
);
}
}
class MyHomePage extends StatelessWidget{
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:AppBar(
title: Text('图片列表')
) ,
body: MyHomeContent(),
);
}
}
class MyHomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
children: [
MyItem(
'kotlin',
'Kotlin 是一门现代但已成熟的编程语言,旨在让开发人员更幸福快乐。 它简洁、安全、可与 Java 及其他语言互操作,并提供了多种方式在多个平台间复用代码,以实现高效编程。',
'https://img1.baidu.com/it/u=3448265981,2295756675&fm=253&fmt=auto&app=138&f=JPEG?w=682&h=343'
),
SizedBox(height: 8,),
MyItem(
'dart',
'Dart 是一种基于类的可选类型化编程语言,设计用于创建 Web 应用程序。 Google 称,Dart 的设计目标是为 Web 编程创造结构化但又富有灵活性的语言;',
'https://img0.baidu.com/it/u=546698500,87821893&fm=253&fmt=auto&app=138&f=PNG?w=1008&h=500'
),
SizedBox(height: 8,),
MyItem(
'swift',
'Swift是苹果公司于2014年WWDC苹果开发者大会发布的新开发语言,可与Objective-C共同运行于macOS和iOS平台,用于搭建基于苹果平台的应用程序。',
'https://img1.baidu.com/it/u=4069946616,401541856&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=374'
),
],
);
}
}
class MyItem extends StatelessWidget{
final String title;
final String info;
final String imgUrl;
// 如果直接写在 Text 内部,每次刷新都会调用,直接提出来
final styleTitle = TextStyle(fontSize: 20,color: Colors.blue,decoration: TextDecoration.none);
final styleInfo = TextStyle(fontSize: 12,color: Colors.red,decoration: TextDecoration.none);
MyItem(this.title,this.info,this.imgUrl);
@override
Widget build(BuildContext context) {
return Container(
// 设置内边距
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
border: Border.all(
width: 2, // 设置边框宽度
color: Colors.blue // 设置边框颜色
)
),
child: Column(
// 子 widget 对齐方式
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title,style: styleTitle),
Text(info,style: styleInfo),
// 设置间距
SizedBox(height: 8.0),
Image.network(imgUrl)
],
),
);
}
}