Skip to main content

最顶层的组件

Scaffold

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Scaffold Example'),
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () {
// 搜索按钮的回调方法
print('Search button pressed!');
},
),
IconButton(
icon: Icon(Icons.settings),
onPressed: () {
// 设置按钮的回调方法
print('Settings button pressed!');
},
),
],
),
body: Center(
child: Text('Hello, World!'),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
decoration: BoxDecoration(
color: Colors.blue,
),
child: Text(
'Drawer Header',
style: TextStyle(
color: Colors.white,
fontSize: 24,
),
),
),
ListTile(
leading: Icon(Icons.home),
title: Text('Home'),
onTap: () {
// Home菜单项的点击回调方法
print('Home menu item tapped!');
},
),
ListTile(
leading: Icon(Icons.favorite),
title: Text('Favorites'),
onTap: () {
// Favorites菜单项的点击回调方法
print('Favorites menu item tapped!');
},
),
ListTile(
leading: Icon(Icons.settings),
title: Text('Settings'),
onTap: () {
// Settings菜单项的点击回调方法
print('Settings menu item tapped!');
},
),
],
),
),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.favorite),
label: 'Favorites',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
),
],
currentIndex: 0,
onTap: (index) {
// 底部导航栏项的点击回调方法
print('Bottom navigation bar item $index tapped!');
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 点击浮动按钮的回调方法
print('Floating action button pressed!');
},
child: Icon(Icons.add),
),
);
}
  • 构建应用页面骨架
  • appBar:导航栏
  • body:内容区域
  • drawer:抽屉菜单
  • bottomNavigationBar:底部导航栏
  • floatingActionButton:浮动按钮

导入包

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
  • 导入了 Material UI 组件库
  • 导入了 Cupertino UI 组件库

应用入口

void main(){
runApp(MyApp());
}
  • runApp 接受一个 Widget 参数
  • MyApp() 是 Flutter 应用的根组件

应用结构

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
  • 定义了一个无状态的 MyApp 类,继承自 StatelessWidget
  • 在 build 方法中,返回了一个 MaterialApp 实例,提供了一些默认主题和路由功能
    • title:应用名称
    • theme:主题配置
    • home:默认的首页,指向一个 MyHomePage 实例

首页

class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});

final String title;

@override
State<MyHomePage> createState() => _MyHomePageState();
}
  • MyHomePage 是一个有状态的 Widget,继承自 StatefulWidget
  • 命名构造函数,用于实例化这个类
    • super.key 表示调用父类的构造函数来设置 key 属性
    • required this.title 表示必须提供 title 参数
  • 接受一个 String 类型的 title 参数
  • 重写了 createState 方法,返回一个 _MyHomePageState 对象

首页状态

class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;

void _incrementCounter() {
setState(() {
_counter++;
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
  • 定义了一个 _MyHomePageState 类,继承自 State\<MyHomePage>
  • _incrementCounter 方法
    • _counter 是一个整数类型的成员变量
  • build 方法返回一个 Scaffold 组件
    • appBar:顶部导航栏
      • Theme.of(context):获取当前主题
    • body:中间区域
      • Column 组件
        • MainAxisAlignment.center:表示在主轴方向上居中对齐
        • Text 组件(第一个):显示静态文本
        • Text 组件(第二个):显示 _counter 的值
    • floatingActionButton:悬浮按钮
      • 响应用户点击事件
  • 将 build 方法放在 State 中,可以实现对有状态组件的高校更新,避免无意义的重绘

路由导航

Navigator.push(context, MaterialPageRoute(builder: (context) {
return NewRoute(text: "我是参数"));
}));


class NewRoute extends StatelessWidget {
const NewRoute({super.key, required this.text});
final String text;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("NewRoute"),
),
body: Center(
child: Text(text),
));
}
}
  • Navigator是一个路由管理组件,push 将一个路由入栈
  • context 是当前 BuildContext 对象的一个引用,用于获取当前 BuildContext 对象
  • MaterialPageRoute 是一个类,用于定义一个新的路由
    • builder 是一个回调函数,用于构建新路由的内容
    • return NewRoute() 是回调函数的具体实现,它返回一个新的组件

路由表导航

Navigator.pushNamed(context, "newRoute", arguments: "hi");


class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
routes: {
"newRoute": (context) => EchoRoute(),
"/": (context) => MyHomePage(title: '首页')
},
);
}
}

class EchoRoute extends StatelessWidget {
const EchoRoute({super.key});

@override
Widget build(BuildContext context) {
var args = ModalRoute.of(context)?.settings.arguments;
String value = args as String;
return NewRoute(text: value);
}
}

使用图片

在 Flutter 项目下 assets/images/1.png

flutter:
  assets:
    - assets/images/1.jpg
Image.asset('assets/images/1.jpg')

子类使用父类方法

Builder(builder: (context) {
return ElevatedButton(
onPressed: () {
// 直接通过of静态方法来获取ScaffoldState
ScaffoldState _state = Scaffold.of(context);
// 打开抽屉菜单
_state.openDrawer();
},
child: Text('打开抽屉菜单'),
);
}),

子组件修改父组件数据

//------------------------ ParentWidget --------------------------------

class ParentWidget extends StatefulWidget {
@override
_ParentWidgetState createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;

void _handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}

@override
Widget build(BuildContext context) {
return Container(
child: TapboxB(
active: _active,
onChanged: _handleTapboxChanged,
),
);
}
}

//------------------------- TapboxB ----------------------------------

class TapboxB extends StatelessWidget {
TapboxB({Key? key, this.active: false, required this.onChanged})
: super(key: key);

final bool active;
final ValueChanged<bool> onChanged;

void _handleTap() {
onChanged(!active);
}

Widget build(BuildContext context) {
return GestureDetector(
onTap: _handleTap,
child: Container(
child: Center(
child: Text(
active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 32.0, color: Colors.white),
),
),
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color: active ? Colors.lightGreen[700] : Colors.grey[600],
),
),
);
}
}

Widget原理

@immutable // 不可变的
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });

final Key? key;

@protected
@factory
Element createElement();

@override
String toStringShort() {
final String type = objectRuntimeType(this, 'Widget');
return key == null ? type : '$type-$key';
}

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
}

@override
@nonVirtual
bool operator ==(Object other) => super == other;

@override
@nonVirtual
int get hashCode => super.hashCode;

static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
...
}
  • @immutable 代表 Widget 是不可变的,因此 Widget 定义的熟悉必须是 final
  • Widget 继承自 DiagnosticableTree(诊断树),主要提供调试信息
  • Key 决定是否在下一次 build 时复用旧的 Widget
  • createElement(核心接口) 生成对应节点对象
  • debugFillProperties 复写父类的方法,主要设置诊断树的一些特性
  • canUpdate 是静态方法,主要在 Widget 树重新 build 时复用旧的 Widget