Skip to content

Flutter GetX 实战手册:从入门到高级

· 7 min

本文每个章节用一个小场景串起 GetX 的关键能力:状态管理、导航与中间件、依赖注入与生命周期、响应式 Workers、网络与存储、主题与国际化、工程化目录与测试。代码可直接复制使用。

目标读者与成果#


0. 快速开始:安装与最小可运行示例#

Terminal window
flutter pub add get
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() => runApp(const MyApp());
class CounterController extends GetxController {
final count = 0.obs;
void inc() => count.value++;
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'GetX Demo',
home: const HomePage(),
getPages: [
GetPage(name: '/', page: () => const HomePage()),
GetPage(name: '/detail', page: () => const DetailPage()),
],
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
final c = Get.put(CounterController());
return Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Center(
child: Obx(() => Text('count: ${c.count.value}')),
),
floatingActionButton: FloatingActionButton(
onPressed: () => c.inc(),
child: const Icon(Icons.add),
),
bottomNavigationBar: ElevatedButton(
onPressed: () => Get.toNamed('/detail'),
child: const Text('Go Detail'),
),
);
}
}
class DetailPage extends StatelessWidget {
const DetailPage({super.key});
@override
Widget build(BuildContext context) {
final c = Get.find<CounterController>();
return Scaffold(
appBar: AppBar(title: const Text('Detail')),
body: Center(child: Obx(() => Text('count: ${c.count}'))),
);
}
}

[!tip] 首次体验


1. 用例:三种状态管理方式怎么选(入门场景)#

需求:计数器 + 用户名输入,感知不同重建粒度与写法。

class AController extends GetxController {
final count = 0.obs;
final name = ''.obs;
}
...
Obx(() => Text('${c.count}'));
GetX<AController>(
init: AController(),
builder: (c) => Text('${c.count}'),
)
class BController extends GetxController {
int count = 0;
void inc() { count++; update(); }
}
...
GetBuilder<BController>(
init: BController(),
builder: (c) => Text('${c.count}'),
)

选择建议


2. 用例:带登录拦截的路由导航(中级场景)#

目标:未登录禁止访问“订单页”,登录后自动跳转回来。

class AuthService extends GetxService {
final loggedIn = false.obs;
Future<AuthService> init() async => this;
}
class AuthMiddleware extends GetMiddleware {
@override
RouteSettings? redirect(String? route) {
final auth = Get.find<AuthService>();
if (!auth.loggedIn.value && route != '/login') {
return const RouteSettings(name: '/login', arguments: {'redirect': '/orders'});
}
return null;
}
}
GetMaterialApp(
initialRoute: '/',
getPages: [
GetPage(name: '/', page: () => const HomePage()),
GetPage(name: '/login', page: () => const LoginPage()),
GetPage(name: '/orders', page: () => const OrdersPage(), middlewares: [AuthMiddleware()]),
],
);
class LoginController extends GetxController {
Future<void> login() async {
final auth = Get.find<AuthService>();
await Future.delayed(const Duration(milliseconds: 400));
auth.loggedIn.value = true;
final redirect = (Get.arguments as Map?)?['redirect'] ?? '/';
Get.offAllNamed(redirect);
}
}

[!tip] 导航速查


3. 用例:依赖注入与生命周期(中级场景)#

Get.put(Controller()); // 立即注入
Get.lazyPut(() => Controller()); // 懒注入(首次 find 时创建)
Get.putAsync(() async => Controller()); // 异步创建
Get.create(() => Controller()); // 每次 find 创建新实例
Get.put<Service>(Service(), permanent: true); // 常驻全局
class DemoController extends GetxController {
@override
void onInit() { super.onInit(); /* 配置监听器 */ }
@override
void onReady() { /* 首次渲染完成 */ }
@override
void onClose() { /* 释放资源 */ }
}
class OrdersBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => OrdersController());
Get.put(Repository());
}
}
GetPage(
name: '/orders',
page: () => const OrdersPage(),
binding: OrdersBinding(),
);

[!warning] 避免内存泄漏


4. 用例:搜索框的防抖与节流(Workers,实战高频)#

需求:用户输入时 300ms 防抖请求;“热门词点击”使用 1s 节流。

class SearchController extends GetxController {
final keyword = ''.obs;
Worker? _debounce, _throttle;
@override
void onInit() {
super.onInit();
_debounce = debounce<String>(keyword, (v) => _fetch(v), time: 300.milliseconds);
_throttle = interval<String>(keyword, (v) => _report(v), time: 1.seconds);
}
void onTapHot(String k) => keyword.value = k;
Future<void> _fetch(String kw) async { /* 请求接口 */ }
void _report(String kw) { /* 上报埋点 */ }
@override
void onClose() {
_debounce?.dispose();
_throttle?.dispose();
super.onClose();
}
}

其他 Workers:ever(每次变化)、once(仅一次)


5. 用例:GetConnect 封装 REST 客户端(中高级)#

class ApiClient extends GetConnect {
@override
void onInit() {
httpClient.baseUrl = 'https://api.example.com';
httpClient.addRequestModifier<dynamic>((request) {
request.headers['Authorization'] = 'Bearer ${Get.find<AuthService>().token}';
return request;
});
httpClient.timeout = const Duration(seconds: 10);
super.onInit();
}
Future<Response<List<Post>>> listPosts() async {
final res = await get('/posts', decoder: (obj) =>
(obj as List).map((e) => Post.fromJson(e)).toList()
);
return res;
}
}
class PostController extends GetxController {
final posts = <Post>[].obs;
final loading = false.obs;
final error = RxnString();
Future<void> refreshList() async {
try {
loading.value = true;
final res = await Get.find<ApiClient>().listPosts();
if (res.isOk && res.body != null) posts.assignAll(res.body!);
else error.value = res.statusText ?? 'Unknown error';
} catch (e) {
error.value = e.toString();
} finally {
loading.value = false;
}
}
}

[!tip] 进阶


6. 用例:GetStorage 做轻量持久化(中级)#

import 'package:get_storage/get_storage.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await GetStorage.init(); // 可传 box 名称
runApp(const MyApp());
}
class Prefs {
static final box = GetStorage();
static const kOnboardingDone = 'onboarding_done';
static bool get done => box.read(kOnboardingDone) ?? false;
static Future<void> setDone() => box.write(kOnboardingDone, true);
}
GetMaterialApp(
initialRoute: Prefs.done ? '/home' : '/onboarding',
getPages: [
GetPage(name: '/onboarding', page: () => const OnboardingPage()),
GetPage(name: '/home', page: () => const HomePage()),
],
);

7. 用例:主题切换与国际化(中级)#

Get.changeTheme(Get.isDarkMode ? ThemeData.light() : ThemeData.dark());
class MyTranslations extends Translations {
@override
Map<String, Map<String, String>> get keys => {
'en_US': {'hello': 'Hello'},
'zh_CN': {'hello': '你好'},
};
}
GetMaterialApp(
translations: MyTranslations(),
locale: const Locale('zh', 'CN'),
fallbackLocale: const Locale('en', 'US'),
);
...
Text('hello'.tr);
Get.updateLocale(const Locale('en', 'US'));

8. 用例:错误重试 + 分页加载(中高级综合)#

class PagingController extends GetxController {
final items = <Item>[].obs;
var page = 1;
final loading = false.obs;
final hasMore = true.obs;
final error = RxnString();
Future<void> loadMore() async {
if (!hasMore.value || loading.value) return;
loading.value = true;
try {
final res = await api.fetch(page);
items.addAll(res.data);
hasMore.value = res.hasMore;
page++;
} catch (e) {
error.value = e.toString();
} finally {
loading.value = false;
}
}
Future<void> retry() => loadMore();
}

UI 片段

Obx(() {
if (c.error.value != null) {
return Column(
children: [
Text('加载失败:${c.error.value}'),
ElevatedButton(onPressed: c.retry, child: const Text('重试')),
],
);
}
return ListView.builder(
itemCount: c.items.length + 1,
itemBuilder: (_, i) {
if (i == c.items.length) {
c.loadMore();
return Center(
child: c.hasMore.isTrue
? const CircularProgressIndicator()
: const Text('没有更多了'),
);
}
return ListTile(title: Text(c.items[i].title));
},
);
})

9. 工程化目录与模块化(中高级)#

建议目录