基于 Dart 的 Flutter 跨平台开发实践
一、Flutter 与 Dart 简介
1.1 Flutter 概述
Flutter 是谷歌开发的一款开源 UI 框架,旨在帮助开发者通过一套代码库,为移动、Web 和桌面等多平台构建高质量、高性能的原生应用。它采用自绘引擎,不依赖于系统原生控件,这使得应用在不同平台上能保持高度一致的外观和性能。Flutter 的优势在于其热重载(Hot Reload)功能,开发者能够快速看到代码修改后的效果,极大地提高了开发效率。同时,Flutter 提供了丰富的预构建 UI 组件库,涵盖从基础的按钮、文本框到复杂的布局和动画组件,降低了开发成本。
1.2 Dart 语言特点
Dart 是一种面向对象的编程语言,由谷歌开发,专为 Flutter 框架设计。它具有以下显著特点:
- 强类型语言:Dart 支持静态类型检查,这有助于在开发阶段发现错误,提高代码的稳定性和可维护性。例如,定义变量时可以明确指定类型:
int age = 25;
String name = "John";
- 单线程异步编程:Dart 使用 isolate 实现隔离的执行环境,避免了多线程编程中的共享状态和锁竞争问题。同时,它通过
async
和await
关键字提供了简洁的异步编程模型。如下是一个简单的异步函数示例:
Future<String> fetchData() async {
// 模拟异步操作,例如网络请求
await Future.delayed(Duration(seconds: 2));
return "Data fetched successfully";
}
- 类和面向对象特性:Dart 支持类的继承、封装和多态。开发者可以定义类并创建对象,如下:
class Person {
String name;
int age;
Person(this.name, this.age);
void sayHello() {
print("Hello, my name is $name and I'm $age years old.");
}
}
- 简洁的语法:Dart 的语法简洁明了,易于学习和使用。例如,它的函数定义和调用方式简洁直观:
int add(int a, int b) {
return a + b;
}
void main() {
int result = add(3, 5);
print(result); // 输出 8
}
二、Flutter 项目搭建
2.1 环境安装
- 安装 Flutter SDK:首先,从 Flutter 官网(https://flutter.dev/docs/get - started/install)下载对应操作系统的 Flutter SDK 压缩包。解压后,将
flutter
目录添加到系统环境变量中。在终端中运行flutter doctor
命令,它会检查并提示安装所需的依赖,如 Android SDK、Xcode(对于 iOS 开发)等。例如,在 Linux 系统下,可通过以下步骤添加环境变量:
export PATH="$PATH:/path/to/flutter/bin"
- 安装 Dart SDK:由于 Flutter 依赖 Dart,通常在安装 Flutter SDK 时,Dart SDK 也会一并安装。可通过运行
dart --version
命令来验证 Dart SDK 是否安装成功。
2.2 创建 Flutter 项目
在安装好 Flutter 环境后,可以使用 flutter create
命令创建一个新的 Flutter 项目。例如,要创建一个名为 my_app
的项目,可在终端中执行以下命令:
flutter create my_app
这将生成一个基本的 Flutter 项目结构,其中 lib
目录存放 Dart 代码,pubspec.yaml
文件用于管理项目依赖。
2.3 项目结构解析
- lib 目录:这是项目的主要代码目录。
main.dart
是项目的入口文件,Flutter 应用从这里开始执行。在main.dart
中,通常会定义一个main
函数,如下:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('My First Flutter App'),
),
body: Center(
child: Text('Hello, Flutter!'),
),
),
);
}
}
- pubspec.yaml:此文件类似于其他项目中的
package.json
文件,用于管理项目的依赖。例如,如果要使用http
库进行网络请求,可以在pubspec.yaml
中添加如下依赖:
dependencies:
flutter:
sdk: flutter
http: ^0.13.4
然后在终端中运行 flutter pub get
命令来获取这些依赖。
三、Flutter 布局与 UI 组件
3.1 布局模型
Flutter 使用基于组件的布局模型,主要有两种布局方式:
- 基于盒模型的布局:Flutter 中的许多组件,如
Container
、Row
、Column
等,遵循盒模型布局。Container
是一个常用的组件,它可以包含其他组件,并设置自身的大小、边距、背景颜色等属性。例如:
Container(
width: 200,
height: 100,
color: Colors.blue,
child: Text('Inside Container'),
margin: EdgeInsets.all(10),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
)
- 基于约束的布局:
Flex
布局(通过Row
和Column
实现)以及Stack
布局属于基于约束的布局。Row
用于水平排列子组件,Column
用于垂直排列子组件。例如,一个简单的Row
布局:
Row(
children: [
Text('Item 1'),
Text('Item 2'),
Text('Item 3'),
],
)
Stack
布局允许子组件堆叠在一起,可以通过 Positioned
组件来定位子组件。例如:
Stack(
children: [
Container(
width: 200,
height: 200,
color: Colors.red,
),
Positioned(
top: 50,
left: 50,
child: Text('Overlay Text'),
)
],
)
3.2 常用 UI 组件
- 文本组件:
Text
组件用于显示文本。可以通过style
属性设置文本的字体、颜色、大小等样式。例如:
Text(
'This is a sample text',
style: TextStyle(
fontSize: 20,
color: Colors.green,
fontWeight: FontWeight.bold,
),
)
- 按钮组件:
ElevatedButton
、TextButton
和OutlinedButton
是常用的按钮组件。ElevatedButton
是带有阴影的填充按钮,TextButton
是文本按钮,OutlinedButton
是带边框的按钮。以下是ElevatedButton
的示例:
ElevatedButton(
onPressed: () {
print('Button pressed');
},
child: Text('Click Me'),
)
- 输入组件:
TextField
用于接收用户输入。可以设置decoration
属性来定制输入框的外观,如添加提示文本、前缀或后缀图标等。例如:
TextField(
decoration: InputDecoration(
hintText: 'Enter your name',
prefixIcon: Icon(Icons.person),
),
)
四、状态管理
4.1 无状态与有状态组件
- 无状态组件(StatelessWidget):无状态组件的状态不会改变,其
build
方法仅根据传入的参数构建 UI。例如,前面提到的MyApp
类就是一个无状态组件。它在构建时,不会因为自身状态的变化而重新构建。
class MyStatelessWidget extends StatelessWidget {
final String message;
MyStatelessWidget(this.message);
@override
Widget build(BuildContext context) {
return Text(message);
}
}
- 有状态组件(StatefulWidget):有状态组件的状态会随着时间或用户交互而改变。要创建一个有状态组件,需要创建一个继承自
StatefulWidget
的类,并同时创建一个继承自State
的内部类。例如,一个简单的计数器应用:
class CounterApp extends StatefulWidget {
@override
_CounterAppState createState() => _CounterAppState();
}
class _CounterAppState extends State<CounterApp> {
int count = 0;
void increment() {
setState(() {
count++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Counter App'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Count: $count'),
ElevatedButton(
onPressed: increment,
child: Text('Increment'),
)
],
),
),
);
}
}
在上述代码中,setState
方法用于通知 Flutter 框架状态发生了变化,从而触发 UI 的重新构建。
4.2 状态管理模式
- InheritedWidget:这是 Flutter 中实现数据共享的一种基础方式。
InheritedWidget
能够在 widget 树中向下传递数据,使得子 widget 可以获取到该数据而无需通过逐层传递参数。例如,创建一个简单的InheritedWidget
来共享一个主题颜色:
class ThemeDataProvider extends InheritedWidget {
final Color themeColor;
ThemeDataProvider({required this.themeColor, required Widget child})
: super(child: child);
static ThemeDataProvider? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ThemeDataProvider>();
}
@override
bool updateShouldNotify(covariant ThemeDataProvider oldWidget) {
return themeColor != oldWidget.themeColor;
}
}
在子组件中,可以通过 ThemeDataProvider.of(context)
获取共享的数据。
2. Provider 包:provider
包是对 InheritedWidget
的进一步封装,提供了更简洁的状态管理方式。首先,在 pubspec.yaml
中添加 provider
依赖:
dependencies:
flutter:
sdk: flutter
provider: ^6.0.5
然后,创建一个数据模型和对应的 ChangeNotifier
:
import 'package:flutter/foundation.dart';
class CounterModel extends ChangeNotifier {
int count = 0;
void increment() {
count++;
notifyListeners();
}
}
在应用中使用 Provider
来提供数据:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => CounterModel(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Provider Counter'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Consumer<CounterModel>(
builder: (context, model, child) {
return Text('Count: ${model.count}');
},
),
ElevatedButton(
onPressed: () {
context.read<CounterModel>().increment();
},
child: Text('Increment'),
)
],
),
),
),
);
}
}
- Bloc 模式:Bloc(Business Logic Component)模式将业务逻辑与 UI 分离。它通过
Bloc
类处理状态变化,通过Event
类触发状态变化。例如,创建一个简单的计数器 Bloc:
import 'package:bloc/bloc.dart';
// 定义事件
abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
// 定义状态
abstract class CounterState {}
class CounterInitialState extends CounterState {}
class CounterUpdatedState extends CounterState {
final int count;
CounterUpdatedState(this.count);
}
// 定义 Bloc
class CounterBloc extends Bloc<CounterEvent, CounterState> {
int count = 0;
CounterBloc() : super(CounterInitialState()) {
on<IncrementEvent>((event, emit) {
count++;
emit(CounterUpdatedState(count));
});
}
}
在 UI 中使用 Bloc:
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
void main() {
runApp(
BlocProvider(
create: (context) => CounterBloc(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Bloc Counter'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
if (state is CounterUpdatedState) {
return Text('Count: ${state.count}');
}
return Text('Initial State');
},
),
ElevatedButton(
onPressed: () {
context.read<CounterBloc>().add(IncrementEvent());
},
child: Text('Increment'),
)
],
),
),
),
);
}
}
五、导航与路由
5.1 基本路由
Flutter 中的路由用于管理页面之间的导航。在 MaterialApp
中,可以通过 routes
属性定义路由表。例如:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
'/': (context) => HomePage(),
'/details': (context) => DetailsPage(),
},
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Page'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/details');
},
child: Text('Go to Details'),
),
),
);
}
}
class DetailsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Details Page'),
),
body: Center(
child: Text('This is the details page'),
),
);
}
}
在上述代码中,Navigator.pushNamed
方法用于根据路由名称导航到指定页面。
5.2 带参数的路由
有时需要在页面之间传递参数。可以通过 Navigator.pushNamed
方法的 arguments
参数传递参数,并在目标页面中通过 ModalRoute.of(context)?.settings.arguments
获取参数。例如:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
'/': (context) => HomePage(),
'/details': (context) => DetailsPage(),
},
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Page'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/details', arguments: 'Hello from Home');
},
child: Text('Go to Details'),
),
),
);
}
}
class DetailsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
String? message = ModalRoute.of(context)?.settings.arguments as String?;
return Scaffold(
appBar: AppBar(
title: Text('Details Page'),
),
body: Center(
child: Text(message?? 'No message received'),
),
);
}
}
5.3 嵌套路由与底部导航栏
- 嵌套路由:在复杂应用中,可能需要在一个页面内嵌套多个路由。例如,在一个底部导航栏应用中,每个导航项可能有自己的路由栈。可以通过
Navigator
组件的onGenerateRoute
属性和Navigator.push
方法实现。如下是一个简单的嵌套路由示例:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: BottomNavigationApp(),
);
}
}
class BottomNavigationApp extends StatefulWidget {
@override
_BottomNavigationAppState createState() => _BottomNavigationAppState();
}
class _BottomNavigationAppState extends State<BottomNavigationApp> {
int _currentIndex = 0;
final List<Widget> _pages = [
PageOne(),
PageTwo(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: _pages[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Settings',
),
],
),
);
}
}
class PageOne extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page One'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PageOneDetails()),
);
},
child: Text('Go to Details'),
),
),
);
}
}
class PageOneDetails extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page One Details'),
),
body: Center(
child: Text('This is the details of Page One'),
),
);
}
}
class PageTwo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page Two'),
),
body: Center(
child: Text('This is Page Two'),
),
);
}
}
- 底部导航栏:如上述代码中,通过
BottomNavigationBar
组件实现了底部导航栏。BottomNavigationBar
的currentIndex
属性用于控制当前显示的页面,onTap
回调函数用于处理用户点击导航项的事件。
六、与原生平台交互
6.1 平台通道(Platform Channels)
Flutter 通过平台通道与原生平台(Android 和 iOS)进行通信。平台通道有三种类型:
- 基本消息通道(BasicMessageChannel):用于传递字符串和半结构化的信息。例如,在 Flutter 端:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class NativeInteractionPage extends StatefulWidget {
@override
_NativeInteractionPageState createState() => _NativeInteractionPageState();
}
class _NativeInteractionPageState extends State<NativeInteractionPage> {
static const platform = BasicMessageChannel(
'samples.flutter.dev/battery',
StandardMessageCodec(),
);
String _batteryLevel = 'Unknown battery level.';
@override
void initState() {
super.initState();
_getBatteryLevel();
}
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
final dynamic result = await platform.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level at $result %.';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Native Interaction'),
),
body: Center(
child: Text(_batteryLevel),
),
);
}
}
在 Android 原生端,需要注册一个方法来响应 Flutter 的调用:
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.BasicMessageChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.StandardMessageCodec;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "samples.flutter.dev/battery";
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler(
(call, result) -> {
if (call.method.equals("getBatteryLevel")) {
int batteryLevel = getBatteryLevel();
if (batteryLevel != -1) {
result.success(batteryLevel);
} else {
result.error("UNAVAILABLE", "Battery level not available.", null);
}
} else {
result.notImplemented();
}
}
);
}
private int getBatteryLevel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
return powerManager.getIntProperty(PowerManager.BATTERY_PROPERTY_CAPACITY);
} else {
return -1;
}
}
}
- 事件通道(EventChannel):用于从原生平台向 Flutter 发送数据流,例如传感器数据。在 Flutter 端:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class SensorDataPage extends StatefulWidget {
@override
_SensorDataPageState createState() => _SensorDataPageState();
}
class _SensorDataPageState extends State<SensorDataPage> {
static const platform = EventChannel('samples.flutter.dev/sensor');
StreamSubscription? _subscription;
String _sensorData = 'No data yet';
@override
void initState() {
super.initState();
_subscribeToSensor();
}
void _subscribeToSensor() {
_subscription = platform.receiveBroadcastStream().listen(
(data) {
setState(() {
_sensorData = 'Sensor data: $data';
});
},
onError: (error) {
setState(() {
_sensorData = 'Error: $error';
});
},
);
}
@override
void dispose() {
_subscription?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Sensor Data'),
),
body: Center(
child: Text(_sensorData),
),
);
}
}
在 Android 原生端,发送传感器数据:
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.EventChannel;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "samples.flutter.dev/sensor";
private SensorManager sensorManager;
private Sensor accelerometer;
private EventChannel.EventSink eventSink;
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new EventChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setStreamHandler(
new EventChannel.StreamHandler() {
@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
eventSink = events;
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(
new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
if (eventSink != null) {
eventSink.success("X: ${event.values[0]}, Y: ${event.values[1]}, Z: ${event.values[2]}");
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
},
accelerometer,
SensorManager.SENSOR_DELAY_NORMAL
);
}
@Override
public void onCancel(Object arguments) {
eventSink = null;
if (sensorManager != null && accelerometer != null) {
sensorManager.unregisterListener(null);
}
}
}
);
}
}
- 方法通道(MethodChannel):用于 Flutter 调用原生平台的方法并获取返回值。前面获取电池电量的示例中就使用了方法通道。
6.2 使用插件
Flutter 生态系统中有许多插件可以简化与原生平台的交互。例如,camera
插件用于访问设备摄像头,geolocator
插件用于获取设备位置信息。要使用插件,首先在 pubspec.yaml
中添加依赖:
dependencies:
flutter:
sdk: flutter
camera: ^0.9.4+14
geolocator: ^9.0.2
然后在 Dart 代码中导入并使用插件:
import 'package:camera/camera.dart';
import 'package:geolocator/geolocator.dart';
class PluginUsagePage extends StatefulWidget {
@override
_PluginUsagePageState createState() => _PluginUsagePageState();
}
class _PluginUsagePageState extends State<PluginUsagePage> {
late CameraController _controller;
Position? _position;
@override
void initState() {
super.initState();
_initCamera();
_getLocation();
}
Future<void> _initCamera() async {
final cameras = await availableCameras();
final firstCamera = cameras.first;
_controller = CameraController(
firstCamera,
ResolutionPreset.medium,
);
await _controller.initialize();
if (!mounted) {
return;
}
setState(() {});
}
Future<void> _getLocation() async {
bool serviceEnabled;
LocationPermission permission;
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
return;
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
return;
}
}
if (permission == LocationPermission.deniedForever) {
return;
}
_position = await Geolocator.getCurrentPosition();
setState(() {});
}
@override
Widget build(BuildContext context) {
if (!_controller.value.isInitialized) {
return const Center(child: CircularProgressIndicator());
}
return Scaffold(
appBar: AppBar(
title: Text('Plugin Usage'),
),
body: Column(
children: [
Expanded(
child: CameraPreview(_controller),
),
if (_position != null)
Text('Latitude: ${_position!.latitude}, Longitude: ${_position!.longitude}')
],
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
七、性能优化
7.1 避免不必要的重建
- 使用 const 组件:如果一个组件在构建过程中不会改变,将其声明为
const
。例如:
const MyConstText = Text('This is a constant text');
这样 Flutter 可以在编译时确定该组件,避免在运行时重复构建。
2. 使用 AnimatedBuilder
:当组件的部分状态变化需要触发动画时,使用 AnimatedBuilder
可以只重建需要更新的部分,而不是整个组件树。例如,一个简单的动画计数器:
import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
class AnimatedCounter extends StatefulWidget {
@override
_AnimatedCounterState createState() => _AnimatedCounterState();
}
class _AnimatedCounterState extends State<AnimatedCounter>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<int> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = IntTween(begin: 0, end: 100).animate(_controller)
..addListener(() {
setState(() {});
});
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Animated Counter'),
),
body: Center(
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Text('Count: ${_animation.value}');
},
),
),
);
}
}
7.2 内存管理
- 及时释放资源:在组件销毁时,及时释放相关资源。例如,在使用
CameraController
时,需要在dispose
方法中调用_controller.dispose()
,如前面插件使用示例中所示。 - 避免内存泄漏:注意处理流(Stream)和订阅(Subscription)。在组件销毁时,取消相关的订阅,例如在事件通道使用示例中,在
dispose
方法中调用_subscription?.cancel()
。
7.3 图片优化
- 使用合适的图片格式:根据图片的用途和显示需求,选择合适的图片格式。例如,对于照片等色彩丰富的图像,使用 JPEG 格式;对于简单的图标,使用 PNG 格式,并且可以考虑使用 WebP 格式以获得更好的压缩比。
- 图片缓存:Flutter 会自动缓存图片,但可以通过
CachedNetworkImage
等插件进一步优化网络图片的加载和缓存。首先在pubspec.yaml
中添加依赖:
dependencies:
flutter:
sdk: flutter
cached_network_image: ^3.2.0
然后在代码中使用:
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
class ImageOptimizationPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Image Optimization'),
),
body: Center(
child: CachedNetworkImage(
imageUrl: 'https://example.com/image.jpg',
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
),
),
);
}
}