Flutter路由传参详解

目录


一、路由传参的基本概念

路由传参是指在Flutter应用中,从一个页面(Widget)导航到另一个页面时,传递数据或参数的过程。

核心原理

  • Flutter的路由系统基于NavigatorRoute
  • 参数传递通过RouteSettings对象实现
  • 目标页面通过ModalRoute.of(context).settings.arguments获取参数

二、路由传参的几种方式

1. 构造函数传参

原理:直接通过目标页面的构造函数传递参数。

实现方式

1
2
3
4
5
6
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(data: 'Hello World'),
),
);

参数获取

1
2
3
4
5
6
7
8
9
10
11
12
class DetailScreen extends StatelessWidget {
final String data;

const DetailScreen({Key? key, required this.data}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Text(data)),
);
}
}

2. RouteSettings传参

原理:通过RouteSettingsarguments属性传递参数。

实现方式

1
2
3
4
5
6
7
8
9
Navigator.push(
context,
MaterialPageRoute(
settings: RouteSettings(
arguments: {'id': 1, 'name': 'Flutter'},
),
builder: (context) => DetailScreen(),
),
);

参数获取

1
2
3
4
5
6
@override
void didChangeDependencies() {
super.didChangeDependencies();
final args = ModalRoute.of(context)?.settings.arguments as Map;
// 处理参数
}

3. 命名路由传参

原理:通过命名路由的arguments参数传递数据。

实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
// 注册路由
MaterialApp(
routes: {
'/detail': (context) => DetailScreen(),
},
);

// 导航并传参
Navigator.pushNamed(
context,
'/detail',
arguments: {'id': 1, 'name': 'Flutter'},
);

参数获取:与RouteSettings传参相同。

4. onGenerateRoute传参

原理:通过onGenerateRoute回调函数处理路由和参数。

实现方式

1
2
3
4
5
6
7
8
9
10
11
MaterialApp(
onGenerateRoute: (settings) {
if (settings.name == '/detail') {
final args = settings.arguments;
return MaterialPageRoute(
builder: (context) => DetailScreen(args: args),
);
}
return null;
},
);

参数获取:通过构造函数获取。

5. 全局状态管理传参

原理:使用Provider、Riverpod等状态管理库传递参数。

实现方式

1
2
3
4
5
6
7
8
9
10
11
// 定义状态
final userProvider = StateProvider<User>((ref) => User());

// 设置状态
context.read(userProvider).state = User(id: 1, name: 'Flutter');

// 导航
Navigator.pushNamed(context, '/detail');

// 获取状态
final user = context.watch(userProvider).state;

三、各种传参方式的比较

传参方式 优点 缺点 适用场景
构造函数传参 1. 类型安全 2. 代码清晰 3. 直接获取 1. 不支持命名路由 2. 参数变更不会触发重建 简单页面,参数较少
RouteSettings传参 1. 支持命名路由 2. 灵活传递各种类型 1. 类型不安全 2. 需要类型转换 复杂参数,命名路由
命名路由传参 1. 路由集中管理 2. 代码简洁 1. 类型不安全 2. 参数获取复杂 应用内页面导航
onGenerateRoute传参 1. 集中处理路由逻辑 2. 支持类型安全 1. 代码复杂度增加 2. 配置繁琐 大型应用,复杂路由
全局状态管理 1. 跨页面共享 2. 实时更新 3. 类型安全 1. 增加依赖 2. 过度设计风险 复杂状态,多页面共享

四、最佳实践

1. 根据场景选择传参方式

  • 简单参数:使用构造函数传参
  • 复杂参数:使用RouteSettings或onGenerateRoute
  • 跨页面共享:使用状态管理

2. 参数获取的最佳时机

不推荐:在initState()中直接获取参数

  • 原因:路由参数可能还未传递到Widget中

推荐

  1. 构造函数传参:直接使用构造函数参数
  2. RouteSettings传参:在didChangeDependencies()中获取
  3. 状态管理:在build()方法中通过watch获取

3. 类型安全

推荐使用类型化的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ScreenArguments {
final int id;
final String name;

ScreenArguments(this.id, this.name);
}

// 传递
Navigator.pushNamed(
context,
'/detail',
arguments: ScreenArguments(1, 'Flutter'),
);

// 获取
final args = ModalRoute.of(context)?.settings.arguments as ScreenArguments;

4. 性能优化

  • 避免在build方法中获取参数:可能导致重复获取
  • 使用didChangeDependencies:只在依赖变化时调用
  • 合理使用状态管理:避免过度使用

五、代码示例

示例1:构造函数传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 导航页面
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(
id: 1,
title: 'Flutter Demo',
),
),
);
},
child: Text('Go to Detail'),
),
),
);
}
}

// 目标页面
class DetailScreen extends StatelessWidget {
final int id;
final String title;

const DetailScreen({Key? key, required this.id, required this.title})
: super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(
child: Text('ID: $id'),
),
);
}
}

示例2:RouteSettings传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 导航页面
Navigator.push(
context,
MaterialPageRoute(
settings: RouteSettings(
arguments: {'id': 1, 'title': 'Flutter Demo'},
),
builder: (context) => DetailScreen(),
),
);

// 目标页面
class DetailScreen extends StatefulWidget {
@override
_DetailScreenState createState() => _DetailScreenState();
}

class _DetailScreenState extends State<DetailScreen> {
late Map<String, dynamic> _args;

@override
void didChangeDependencies() {
super.didChangeDependencies();
_args = ModalRoute.of(context)?.settings.arguments as Map;
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(_args['title'])),
body: Center(
child: Text('ID: ${_args['id']}'),
),
);
}
}

示例3:命名路由传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 注册路由
MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => HomeScreen(),
'/detail': (context) => DetailScreen(),
},
);

// 导航
Navigator.pushNamed(
context,
'/detail',
arguments: ScreenArguments(1, 'Flutter Demo'),
);

// 目标页面
class DetailScreen extends StatefulWidget {
@override
_DetailScreenState createState() => _DetailScreenState();
}

class _DetailScreenState extends State<DetailScreen> {
late ScreenArguments _args;

@override
void didChangeDependencies() {
super.didChangeDependencies();
_args = ModalRoute.of(context)?.settings.arguments as ScreenArguments;
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(_args.title)),
body: Center(
child: Text('ID: ${_args.id}'),
),
);
}
}

class ScreenArguments {
final int id;
final String title;

ScreenArguments(this.id, this.title);
}

示例4:onGenerateRoute传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 配置路由
MaterialApp(
onGenerateRoute: (settings) {
switch (settings.name) {
case '/detail':
final args = settings.arguments as ScreenArguments;
return MaterialPageRoute(
builder: (context) => DetailScreen(args: args),
);
default:
return MaterialPageRoute(builder: (context) => HomeScreen());
}
},
);

// 导航
Navigator.pushNamed(
context,
'/detail',
arguments: ScreenArguments(1, 'Flutter Demo'),
);

// 目标页面
class DetailScreen extends StatelessWidget {
final ScreenArguments args;

const DetailScreen({Key? key, required this.args}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(args.title)),
body: Center(
child: Text('ID: ${args.id}'),
),
);
}
}

示例5:全局状态管理传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 定义状态
final userProvider = StateProvider<User>((ref) => User());

// 设置状态
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
context.read(userProvider).state = User(id: 1, name: 'Flutter');
Navigator.pushNamed(context, '/detail');
},
child: Text('Go to Detail'),
),
),
);
}
}

// 获取状态
class DetailScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userProvider).state;
return Scaffold(
appBar: AppBar(title: Text(user.name)),
body: Center(
child: Text('ID: ${user.id}'),
),
);
}
}

class User {
final int id;
final String name;

User({this.id = 0, this.name = ''});
}

总结

Flutter提供了多种路由传参方式,每种方式都有其适用场景:

  1. 构造函数传参:简单直接,类型安全,适用于简单页面
  2. RouteSettings传参:灵活多样,支持命名路由,适用于复杂参数
  3. 命名路由传参:集中管理,代码简洁,适用于应用内导航
  4. onGenerateRoute传参:集中处理,支持类型安全,适用于大型应用
  5. 全局状态管理:跨页面共享,实时更新,适用于复杂状态

选择合适的传参方式需要考虑:

  • 参数的复杂度和类型
  • 页面间的关系
  • 代码的可维护性
  • 性能要求

通过合理选择和组合使用这些方式,可以构建出清晰、高效的Flutter应用导航系统。

Flutter 路由详解

目录


一、路由基础概念

1. 什么是路由?

路由(Route)是指应用中页面之间的导航机制,在Flutter中,路由是由Navigator组件管理的Route对象的堆栈。

2. 核心组件

  • Navigator:管理路由堆栈的核心类,负责页面的推入、弹出和替换
  • Route:表示一个页面的抽象类,常用实现有MaterialPageRouteCupertinoPageRoute
  • RouteSettings:包含路由的配置信息,如路由名称和参数

3. 路由堆栈

  • 推入(push):将新页面添加到堆栈顶部
  • 弹出(pop):移除堆栈顶部的页面
  • 替换(replace):用新页面替换堆栈顶部的页面
  • 清空(popUntil):弹出页面直到指定条件

二、基本导航方式

1. 基本导航

推入新页面

1
2
3
4
5
6
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(),
),
);

返回上一页

1
Navigator.pop(context);

带返回值的导航

1
2
3
4
5
6
7
8
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SelectionScreen(),
),
);
// 处理返回结果
print('Result: $result');

2. 替换路由

1
2
3
4
5
6
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => NewScreen(),
),
);

3. 移除指定路由

1
2
3
4
Navigator.popUntil(
context,
ModalRoute.withName('/home'),
);

4. 清空堆栈并导航

1
2
3
4
5
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => LoginScreen()),
(Route<dynamic> route) => false, // 移除所有路由
);

三、路由传参详解

1. 构造函数传参

传递参数

1
2
3
4
5
6
7
8
9
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(
id: 1,
title: 'Flutter Demo',
),
),
);

接收参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class DetailScreen extends StatelessWidget {
final int id;
final String title;

const DetailScreen({Key? key, required this.id, required this.title})
: super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(child: Text('ID: $id')),
);
}
}

2. RouteSettings传参

传递参数

1
2
3
4
5
6
7
8
9
Navigator.push(
context,
MaterialPageRoute(
settings: RouteSettings(
arguments: {'id': 1, 'title': 'Flutter Demo'},
),
builder: (context) => DetailScreen(),
),
);

接收参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class DetailScreen extends StatefulWidget {
@override
_DetailScreenState createState() => _DetailScreenState();
}

class _DetailScreenState extends State<DetailScreen> {
late Map<String, dynamic> _args;

@override
void didChangeDependencies() {
super.didChangeDependencies();
_args = ModalRoute.of(context)?.settings.arguments as Map;
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(_args['title'])),
body: Center(child: Text('ID: ${_args['id']}')),
);
}
}

3. 类型安全的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ScreenArguments {
final int id;
final String title;

ScreenArguments(this.id, this.title);
}

// 传递
Navigator.push(
context,
MaterialPageRoute(
settings: RouteSettings(
arguments: ScreenArguments(1, 'Flutter Demo'),
),
builder: (context) => DetailScreen(),
),
);

// 接收
final args = ModalRoute.of(context)?.settings.arguments as ScreenArguments;

四、命名路由管理

1. 基本配置

1
2
3
4
5
6
7
8
MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => HomeScreen(),
'/second': (context) => SecondScreen(),
'/detail': (context) => DetailScreen(),
},
);

2. 导航到命名路由

1
Navigator.pushNamed(context, '/second');

3. 带参数的命名路由

1
2
3
4
5
Navigator.pushNamed(
context,
'/detail',
arguments: ScreenArguments(1, 'Flutter Demo'),
);

4. onGenerateRoute

用于处理未在routes中注册的路由,或需要动态生成的路由:

1
2
3
4
5
6
7
8
9
10
11
12
MaterialApp(
onGenerateRoute: (settings) {
if (settings.name == '/detail') {
final args = settings.arguments as ScreenArguments;
return MaterialPageRoute(
builder: (context) => DetailScreen(args: args),
);
}
// 处理其他路由...
return MaterialPageRoute(builder: (context) => NotFoundScreen());
},
);

5. onUnknownRoute

onGenerateRoute无法处理路由时调用:

1
2
3
4
5
MaterialApp(
onUnknownRoute: (settings) {
return MaterialPageRoute(builder: (context) => NotFoundScreen());
},
);

五、路由守卫与拦截

1. 自定义路由拦截

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class AuthGuard extends StatelessWidget {
final Widget child;

const AuthGuard({Key? key, required this.child}) : super(key: key);

@override
Widget build(BuildContext context) {
final isLoggedIn = AuthService.isLoggedIn();

if (!isLoggedIn) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => LoginScreen()),
);
});
}

return child;
}
}

2. 路由监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class RouteObserverExample extends StatefulWidget {
@override
_RouteObserverExampleState createState() => _RouteObserverExampleState();
}

class _RouteObserverExampleState extends State<RouteObserverExample> with RouteAware {
static final routeObserver = RouteObserver<PageRoute>();

@override
void didChangeDependencies() {
super.didChangeDependencies();
routeObserver.subscribe(this, ModalRoute.of(context)! as PageRoute);
}

@override
void dispose() {
routeObserver.unsubscribe(this);
super.dispose();
}

@override
void didPush() {
// 路由被推入时调用
print('Route pushed');
}

@override
void didPop() {
// 路由被弹出时调用
print('Route popped');
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Route Observer')),
body: Center(child: Text('Route Observer Example')),
);
}
}

// 在MaterialApp中注册
MaterialApp(
navigatorObservers: [RouteObserverExample.routeObserver],
);

六、深度链接与通用链接

1. 配置深度链接

Android配置 (AndroidManifest.xml):

1
2
3
4
5
6
7
8
9
10
11
12
13
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="myapp"
android:host="example.com"
android:pathPrefix="/detail" />
</intent-filter>
</activity>

iOS配置 (Info.plist):

1
2
3
4
5
6
7
8
9
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>

2. 处理深度链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
MaterialApp(
initialRoute: '/',
onGenerateRoute: (settings) {
if (settings.name?.startsWith('/detail/') ?? false) {
// 解析路径参数
final pathSegments = Uri.parse(settings.name!).pathSegments;
final id = pathSegments[1];
return MaterialPageRoute(
builder: (context) => DetailScreen(id: id),
);
}
// 其他路由处理...
},
);

3. 使用uni_links库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import 'package:uni_links/uni_links.dart';

class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
StreamSubscription? _linkSubscription;

@override
void initState() {
super.initState();
_initDeepLinks();
}

void _initDeepLinks() async {
// 处理初始链接
final initialLink = await getInitialLink();
_handleDeepLink(initialLink);

// 监听后续链接
_linkSubscription = linkStream.listen((String? link) {
_handleDeepLink(link);
});
}

void _handleDeepLink(String? link) {
if (link != null) {
final uri = Uri.parse(link);
if (uri.path.startsWith('/detail')) {
final id = uri.queryParameters['id'];
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailScreen(id: id)),
);
}
}
}

@override
void dispose() {
_linkSubscription?.cancel();
super.dispose();
}

@override
Widget build(BuildContext context) {
return MaterialApp(
// ...
);
}
}

七、路由动画与过渡效果

1. 自定义页面过渡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => DetailScreen(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(1.0, 0.0);
const end = Offset.zero;
const curve = Curves.ease;

var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));

return SlideTransition(
position: animation.drive(tween),
child: child,
);
},
),
);

2. 预定义过渡效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 淡入淡出
Navigator.push(
context,
PageRouteBuilder(
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: animation,
child: child,
);
},
pageBuilder: (context, animation, secondaryAnimation) => DetailScreen(),
),
);

// 缩放
Navigator.push(
context,
PageRouteBuilder(
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return ScaleTransition(
scale: animation,
child: child,
);
},
pageBuilder: (context, animation, secondaryAnimation) => DetailScreen(),
),
);

3. 平台特定过渡

1
2
3
4
5
6
7
8
9
10
11
// Android风格
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailScreen()),
);

// iOS风格
Navigator.push(
context,
CupertinoPageRoute(builder: (context) => DetailScreen()),
);

八、嵌套路由与Tab导航

1. 嵌套Navigator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class HomeScreen extends StatelessWidget {
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home')),
body: Navigator(
key: _navigatorKey,
initialRoute: '/home',
onGenerateRoute: (settings) {
switch (settings.name) {
case '/home':
return MaterialPageRoute(builder: (context) => HomeContent());
case '/home/detail':
return MaterialPageRoute(builder: (context) => HomeDetail());
default:
return null;
}
},
),
);
}
}

2. Tab导航与路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
class TabNavigator extends StatelessWidget {
final String tabItem;
final GlobalKey<NavigatorState> navigatorKey;

const TabNavigator({
Key? key,
required this.tabItem,
required this.navigatorKey,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
initialRoute: '/',
onGenerateRoute: (settings) {
Widget child;
switch (settings.name) {
case '/':
child = getTabScreen(tabItem);
break;
case '/detail':
child = DetailScreen();
break;
default:
child = getTabScreen(tabItem);
}
return MaterialPageRoute(builder: (context) => child);
},
);
}

Widget getTabScreen(String tabItem) {
switch (tabItem) {
case 'home':
return HomeScreen();
case 'profile':
return ProfileScreen();
default:
return HomeScreen();
}
}
}

class MainScreen extends StatefulWidget {
@override
_MainScreenState createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
String _currentTab = 'home';
final Map<String, GlobalKey<NavigatorState>> _navigatorKeys = {
'home': GlobalKey<NavigatorState>(),
'profile': GlobalKey<NavigatorState>(),
};

void _selectTab(String tabItem) {
if (tabItem == _currentTab) {
_navigatorKeys[tabItem]?.currentState?.popUntil((route) => route.isFirst);
} else {
setState(() => _currentTab = tabItem);
}
}

@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
final isFirstRouteInCurrentTab = !await _navigatorKeys[_currentTab]!.currentState!.maybePop();
if (isFirstRouteInCurrentTab) {
if (_currentTab != 'home') {
_selectTab('home');
return false;
}
}
return isFirstRouteInCurrentTab;
},
child: Scaffold(
body: Stack(children: [
_buildOffstageNavigator('home'),
_buildOffstageNavigator('profile'),
]),
bottomNavigationBar: BottomNavigationBar(
currentIndex: ['home', 'profile'].indexOf(_currentTab),
onTap: (index) => _selectTab(['home', 'profile'][index]),
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
],
),
),
);
}

Widget _buildOffstageNavigator(String tabItem) {
return Offstage(
offstage: _currentTab != tabItem,
child: TabNavigator(
tabItem: tabItem,
navigatorKey: _navigatorKeys[tabItem]!,
),
);
}
}

九、路由性能优化

1. 延迟加载路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
MaterialApp(
onGenerateRoute: (settings) {
if (settings.name == '/heavy') {
return MaterialPageRoute(builder: (context) {
// 延迟加载重量级页面
return FutureBuilder(
future: Future.delayed(Duration(milliseconds: 100)),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return HeavyScreen();
}
return Center(child: CircularProgressIndicator());
},
);
});
}
// 其他路由...
},
);

2. 路由预加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class PreloadManager {
static final Map<String, Widget> _preloadedWidgets = {};

static Future<void> preload(String routeName) async {
switch (routeName) {
case '/detail':
_preloadedWidgets['/detail'] = DetailScreen();
break;
case '/settings':
_preloadedWidgets['/settings'] = SettingsScreen();
break;
}
}

static Widget? getPreloaded(String routeName) {
return _preloadedWidgets[routeName];
}
}

// 使用
await PreloadManager.preload('/detail');

// 导航时使用预加载的Widget
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PreloadManager.getPreloaded('/detail')!),
);

3. 路由缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class RouteCache {
static final Map<String, Widget> _cache = {};

static Widget getOrCreate(String routeName, Widget Function() creator) {
if (!_cache.containsKey(routeName)) {
_cache[routeName] = creator();
}
return _cache[routeName]!;
}

static void clear() {
_cache.clear();
}

static void remove(String routeName) {
_cache.remove(routeName);
}
}

// 使用
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RouteCache.getOrCreate(
'/detail',
() => DetailScreen(),
),
),
);

十、最佳实践

1. 路由管理架构

推荐的路由管理方式

  • 使用命名路由进行集中管理
  • 结合onGenerateRoute处理动态路由
  • 使用类型安全的参数传递

目录结构

1
2
3
4
5
6
7
8
9
10
lib/
├── routes/
│ ├── route_names.dart # 路由名称常量
│ ├── route_arguments.dart # 路由参数类型
│ └── route_generator.dart # 路由生成器
├── screens/
│ ├── home_screen.dart
│ ├── detail_screen.dart
│ └── ...
└── main.dart

2. 路由名称管理

1
2
3
4
5
6
7
// route_names.dart
class RouteNames {
static const String home = '/';
static const String detail = '/detail';
static const String settings = '/settings';
static const String profile = '/profile';
}

3. 路由参数类型

1
2
3
4
5
6
7
8
9
10
11
12
13
// route_arguments.dart
class DetailArguments {
final int id;
final String title;

DetailArguments(this.id, this.title);
}

class ProfileArguments {
final String userId;

ProfileArguments(this.userId);
}

4. 路由生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// route_generator.dart
import 'package:flutter/material.dart';
import 'package:your_app/screens/home_screen.dart';
import 'package:your_app/screens/detail_screen.dart';
import 'package:your_app/screens/settings_screen.dart';
import 'package:your_app/screens/profile_screen.dart';
import 'package:your_app/routes/route_names.dart';
import 'package:your_app/routes/route_arguments.dart';

class RouteGenerator {
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case RouteNames.home:
return MaterialPageRoute(builder: (_) => HomeScreen());

case RouteNames.detail:
final args = settings.arguments as DetailArguments;
return MaterialPageRoute(
builder: (_) => DetailScreen(args: args),
);

case RouteNames.settings:
return MaterialPageRoute(builder: (_) => SettingsScreen());

case RouteNames.profile:
final args = settings.arguments as ProfileArguments;
return MaterialPageRoute(
builder: (_) => ProfileScreen(args: args),
);

default:
return _errorRoute();
}
}

static Route<dynamic> _errorRoute() {
return MaterialPageRoute(builder: (_) {
return Scaffold(
appBar: AppBar(title: Text('Error')),
body: Center(child: Text('Page not found')),
);
});
}
}

5. 应用配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// main.dart
import 'package:flutter/material.dart';
import 'package:your_app/routes/route_generator.dart';
import 'package:your_app/routes/route_names.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Route Demo',
initialRoute: RouteNames.home,
onGenerateRoute: RouteGenerator.generateRoute,
);
}
}

6. 导航工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class NavigationService {
static GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

static Future<dynamic> navigateTo(String routeName, {dynamic arguments}) {
return navigatorKey.currentState!.pushNamed(routeName, arguments: arguments);
}

static void goBack() {
navigatorKey.currentState!.pop();
}

static Future<dynamic> replaceWith(String routeName, {dynamic arguments}) {
return navigatorKey.currentState!.pushReplacementNamed(routeName, arguments: arguments);
}

static void popUntil(String routeName) {
navigatorKey.currentState!.popUntil(ModalRoute.withName(routeName));
}

static Future<dynamic> pushAndRemoveUntil(String routeName, {dynamic arguments}) {
return navigatorKey.currentState!.pushNamedAndRemoveUntil(
routeName,
(Route<dynamic> route) => false,
arguments: arguments,
);
}
}

// 配置
MaterialApp(
navigatorKey: NavigationService.navigatorKey,
// ...
);

// 使用
NavigationService.navigateTo(RouteNames.detail, arguments: DetailArguments(1, 'Flutter'));

总结

Flutter的路由系统是一个强大而灵活的导航框架,通过掌握以下核心概念和技术,可以构建出流畅、高效的应用导航体验:

  1. 基础导航:掌握Navigator的基本操作
  2. 参数传递:选择合适的传参方式
  3. 命名路由:实现集中化的路由管理
  4. 路由守卫:处理权限和导航逻辑
  5. 深度链接:支持外部链接打开应用
  6. 路由动画:提升用户体验
  7. 嵌套路由:实现复杂的导航结构
  8. 性能优化:确保导航流畅性

通过合理的路由设计和管理,可以显著提升应用的用户体验和代码可维护性。希望这份指南能帮助你更好地理解和使用Flutter的路由系统。

Flutter 到区块链:分层架构与交互全流程

本文从移动端工程视角,说明 Flutter + 原生 + 密码学库(如 Trust Wallet Core)+ 节点 RPC + 区块链 在用户完成一笔链上交易时的典型分工与数据流。适用于自托管钱包、DEX 集成等场景的架构理解(具体产品实现可能有网关、风控等差异)。

1. 先厘清三件事

  1. 区块链节点不会直接和你的手机 App「长连接握手」完成业务;App 通常通过 HTTPS 上的 JSON-RPC(或 REST)远程节点通信。
  2. Trust Wallet Core 一类库,主体多为 C++,负责 地址派生、交易构造、数字签名、按链协议序列化不负责替你选公共节点或发起 HTTP(由宿主 App 实现)。
  3. Flutter 负责 UI 与业务状态;涉及 私钥、签名 时,常见做法是放到 iOS/Android 原生(Keychain / Keystore + JNI),再通过 MethodChannel 与 Dart 通信。

2. 分层总览:谁做什么

层级 典型技术 职责
表现层 Flutter Widget 表单、确认弹窗、加载态、结果页
业务 / 状态 Bloc / Riverpod / Provider 等 参数校验、组装请求模型、调用 Channel
跨端桥 MethodChannel / Pigeon / FFI Dart ↔ 原生类型与异步传递
原生 Kotlin / Swift 安全存储、调 TWC 绑定、HTTP 客户端、证书策略
密码学 / 链协议 Trust Wallet Core(C++ .so / xcframework) 未签名交易 → 签名 → 原始交易字节 / hex
网络 OkHttp / URLSession / Dio RPC:读 nonce、余额、estimateGas;写 eth_sendRawTransaction
公链节点集群 验证交易、广播、mempool、出块

核心结论:「上链」在工程上 = 库内完成签名 + 网络层把已签名交易发给节点


3. 架构图:从界面到链

flowchart TB
  subgraph UI["表现层"]
    F["Flutter Widget\n按钮、金额、链选择、确认弹窗"]
  end

  subgraph VM["业务层"]
    B["Flutter 业务逻辑\n校验、状态机、组装 DTO"]
  end

  subgraph Bridge["跨端桥"]
    C["MethodChannel / Pigeon"]
  end

  subgraph Native["原生层"]
    N1["Kotlin / Swift\nKeychain / Keystore、权限"]
    N2["Trust Wallet Core\n(C++ 动态库)\n交易体 + 签名"]
  end

  subgraph Net["网络层"]
    H["HTTP 客户端"]
    RPC["节点 RPC\nJSON-RPC / REST"]
  end

  subgraph Chain["区块链"]
    BC["共识与出块"]
  end

  F --> B --> C --> N1 --> N2
  N2 -->|"signed tx"| N1
  N1 --> H --> RPC --> BC

4. 主流程时序图:用户点击「确认」之后

以下为 签名在原生、RPC 也由原生(或统一网络层)发起 的常见形态,便于统一钉证书与审计日志。

sequenceDiagram
  autonumber
  participant U as 用户
  participant W as Flutter UI
  participant B as Flutter 业务层
  participant CH as MethodChannel
  participant N as 原生
  participant TWC as Trust Wallet Core
  participant RPC as 节点 RPC
  participant BC as 区块链

  U->>W: 点击确认交易
  W->>B: 提交 to / amount / chainId 等
  B->>B: 校验与组装参数

  B->>CH: invokeMethod(signAndSend / buildTx, args)
  CH->>N: 传递到原生

  N->>N: 解锁密钥材料(生物识别 / PIN)
  N->>RPC: 读链:nonce、fee、余额等
  RPC-->>N: 返回值

  N->>TWC: 构造未签名交易并签名
  TWC-->>N: 已签名交易 hex / bytes

  N->>RPC: 写链:eth_sendRawTransaction 等
  RPC->>BC: 广播
  BC-->>RPC: 返回 tx hash
  RPC-->>N: tx hash

  N-->>CH: { txHash, error? }
  CH-->>B: Future 完成
  B-->>W: 更新 UI
  W-->>U: 展示哈希 / 区块浏览器

步骤解读(与上图序号对应)

  1. 用户操作:仅在 UI 层。
  2. 业务层:做产品规则校验(限额、格式),不碰私钥明文。
  3. Channel:把「意图」和「非敏感参数」交给原生;不要把私钥字符串在 Dart 里传来传去
  4. 原生:从安全存储取密钥或触发系统认证。
  5. 读链 RPC:例如 EVM 链上需要先拿 nonce、估算 gas;这些请求 与签名无关,走 HTTP 即可。
  6. TWC:输入链类型、账户、目标交易内容,输出 签名后的原始交易
  7. 写链 RPC:将 已签名交易 发给节点;节点再进入 mempool。
  8. :矿工/验证者打包,产生确认数。

5. 变体:原生只签名,Flutter 发 RPC

部分团队会让 Dart 层用 Dio 直接调 Infura / Alchemy / 自建节点,原生 MethodChannel 只返回 signedHex

sequenceDiagram
  participant B as Flutter
  participant CH as MethodChannel
  participant N as 原生
  participant TWC as Trust Wallet Core
  participant RPC as 节点

  B->>CH: signOnly(unsignedTxParams)
  CH->>N: 
  N->>TWC: 签名
  TWC-->>N: signed hex
  N-->>CH: 
  CH-->>B: signed hex

  B->>RPC: eth_sendRawTransaction(signedHex)
  RPC-->>B: tx hash

注意:私钥仍应只在原生 + TWC 边界内使用;Dart 侧只拿 已签名 的十六进制串,不要把助记词传到 Flutter。


6. 数据流简图:字节往哪走

flowchart LR
  subgraph in["输入"]
    A1["用户输入\n地址、金额"]
    A2["链与合约参数\nchainId、ABI、nonce"]
  end

  subgraph twc["Trust Wallet Core"]
    S["编码 + 签名\n→ raw transaction"]
  end

  subgraph out["对外"]
    H["0x… 已签名交易"]
    TX["交易哈希"]
  end

  A1 --> S
  A2 --> S
  S --> H
  H -->|"HTTPS JSON-RPC"| RPC["节点"]
  RPC --> TX

7. Trust Wallet Core 与「和链交互」的关系

  • 开发语言:核心为 C++;Android 为 libTrustWalletCore.so,iOS 为 xcframework;通过 Kotlin / Swift 绑定 调用,而非在 App 内用 JS 实现核心链逻辑。
  • 和链的交互方式
    • 库内部:密码学、交易结构、签名算法、各链差异。
    • 库外部:你的 App 用 RPCsigned transaction只读请求(余额、nonce)发到节点;协议多为 JSON-RPC(如以太坊系)。

因此:「与区块链交互」在实现上 = RPC 上的读写TWC 负责产生可被链接受的那串字节


8. 安全与工程要点

  • 私钥与助记词:优先 Keychain / Keystore / 硬件模块;Dart 内存尽量不出现明文。
  • Channel 传参:传 交易参数、链 ID、签名结果,不传私钥。
  • RPC 端点:生产环境常配合 证书钉扎、代理、自有网关,防止中间人篡改 sendRawTransaction 的返回。
  • 重放与链切换:正确设置 chainId(EIP-155),避免跨链重放。

9. 与「交易所托管」模式的对比(扩展)

若业务是 CEX 托管钱包,时序里往往在 用户确认 之后增加 你们服务端:风控、限额、内部记账,再由服务端或专用签名机与链交互。此时上图中的 RPC 可能变为 先调业务 API,与纯 自托管 + TWC 的图不同。理解分层后,可按实际产品替换「RPC 前」的一跳。


10. 小结

问题 答案
Flutter 怎么「上链」? 多数情况下 不直接上链,而是 调原生签名 + HTTP 调节点 RPC
TWC 做什么? 交易构造与签名(C++ 库)。
链从哪里来? 远程节点返回区块与收据;广播即节点接受你的 sendRawTransaction
JS 是不是核心? 不是;可选 WASM/TS 绑定,核心仍是 C++。

文档为通用架构说明,便于学习与工程对照;具体 App 的网关、风控、多签等需以实际系统为准。

React / Flutter 状态管理

由浅入深,从基本概念到原理与源码,再到示例与实际项目应用案例,系统梳理两大主流框架中的状态管理方案

一、状态管理基础概念

1.1 什么是状态(State)?

状态是驱动 UI 变化的数据。当状态改变时,界面随之更新,形成「数据驱动视图」的声明式模式。

1
2
3
4
状态 ──► 视图
▲ │
│ ▼
└── 用户交互 / 网络请求 / 定时器等

1.2 状态的分类

类型 作用域 典型场景 生命周期
本地状态 单组件 输入框内容、展开/折叠、选中项 跟随组件
共享状态 多组件 用户信息、主题、购物车 需要提升或全局管理
服务端状态 与后端同步 API 数据、缓存 异步、需缓存策略

1.3 为什么需要状态管理?

随着应用复杂度上升,会出现:

  • 状态提升导致 props 层层传递(prop drilling)
  • 状态分散导致难以追踪和调试
  • 重复请求缓存失效等数据一致性问题

状态管理方案的目标:集中、可预测、易维护


二、React 状态管理

2.1 内置方案概览

方案 适用场景 特点
useState 本地状态 简单、轻量
useReducer 复杂本地状态 可预测、易测试
Context API 跨层级共享 官方内置、易造成不必要的重渲染

2.2 useState:最简单的本地状态

1
2
3
4
5
6
7
8
9
10
11
12
function Counter() {
const [count, setCount] = useState(0);

return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
{/* 函数式更新,避免闭包陷阱 */}
<button onClick={() => setCount(prev => prev + 1)}>+1 (安全)</button>
</div>
);
}

惰性初始化:初始值可以是函数,仅在首次渲染执行。

1
const [state, setState] = useState(() => expensiveComputation());

2.3 useReducer:复杂状态的 reducer 模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}

function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });

return (
<div>
<span>{state.count}</span>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
}

2.4 Context API:跨层级共享状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const ThemeContext = createContext('light');

function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Page />
</ThemeContext.Provider>
);
}

function Page() {
const { theme } = useContext(ThemeContext);
return <div className={theme}>...</div>;
}

注意:Provider 的 value 变化会导致所有 useContext 的消费者重渲染,需配合 useMemo 或拆分 Context 优化。

2.5 Redux / Redux Toolkit:全局状态管理

Redux 采用单向数据流View → Action → Reducer → Store → View

1
2
3
4
5
┌─────────┐   dispatch    ┌─────────┐   reduce    ┌────────┐
│ View │ ───────────► │ Action │ ─────────► │ Store │
└─────────┘ └─────────┘ └────────┘
▲ │
└──────────────── subscribe ─────────────────────┘

Redux Toolkit 示例(官方推荐写法):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// store/counterSlice.js
import { createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: state => { state.value += 1; },
decrement: state => { state.value -= 1; },
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
});

export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
1
2
3
4
5
6
7
8
9
10
// 组件中使用
import { useDispatch, useSelector } from 'react-redux';
import { increment } from './store/counterSlice';

function Counter() {
const count = useSelector(state => state.counter.value);
const dispatch = useDispatch();

return <button onClick={() => dispatch(increment())}>{count}</button>;
}

2.6 Zustand:轻量级全局状态

Zustand 基于 Hooks,API 简洁,无 Provider 包裹。

1
2
3
4
5
6
7
8
9
10
11
12
import { create } from 'zustand';

const useStore = create((set) => ({
count: 0,
increment: () => set(state => ({ count: state.count + 1 })),
decrement: () => set(state => ({ count: state.count - 1 })),
}));

function Counter() {
const { count, increment } = useStore();
return <button onClick={increment}>{count}</button>;
}

选择器优化:只订阅需要的字段,避免无关更新。

1
const count = useStore(state => state.count); // 仅 count 变化时重渲染

2.7 React 状态管理原理浅析

useState 的链表结构

React 内部用链表存储 Hooks。每个 Hook 对应链表中的一个节点,通过 FibermemoizedState 串联。

1
2
3
Fiber.memoizedState → Hook1 → Hook2 → Hook3 → ...

[state, setState]

这就是为什么 Hooks 必须在顶层调用、不能放在条件/循环中:链表顺序必须稳定。

setState 的批处理(Batching)

React 18 默认对所有更新进行自动批处理,多次 setState 会合并为一次渲染。

1
2
3
4
5
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// 仅触发一次重渲染
}

三、Flutter 状态管理

3.1 方案概览

方案 官方/社区 适用场景 特点
setState 内置 本地状态 简单,整组件重建
InheritedWidget 内置 跨层级共享 底层基础,一般不直接使用
Provider 官方推荐 中小型应用 基于 InheritedWidget,易上手
Riverpod 社区主流 中大型应用 编译期安全、可测试、无 context
Bloc 社区 复杂业务逻辑 事件驱动、可预测、适合团队
GetX 社区 快速开发 全能型,状态+路由+依赖注入

3.2 setState:最简单的本地状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Counter extends StatefulWidget {
@override
_CounterState createState() => _CounterState();
}

class _CounterState extends State<Counter> {
int _count = 0;

void _increment() {
setState(() {
_count++;
});
}

@override
Widget build(BuildContext context) {
return Column(
children: [
Text('$_count'),
ElevatedButton(onPressed: _increment, child: Text('+1')),
],
);
}
}

原理setState 会标记当前 Element 为脏,在下一帧触发 build 重建子树。

3.3 Provider:官方推荐方案

Provider 基于 InheritedWidget,通过 context.watch<T>() 监听变化并重建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 1. 定义 Model(继承 ChangeNotifier)
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;

void increment() {
_count++;
notifyListeners(); // 通知监听者
}
}

// 2. 在根节点提供
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CounterModel(),
child: MyApp(),
),
);
}

// 3. 在子组件使用
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = context.watch<CounterModel>();
return Text('${counter.count}');
}
}

多种 Provider 类型

类型 用途
Provider 不可变值
ChangeNotifierProvider 可变、需 notifyListeners
FutureProvider 异步数据
StreamProvider 流数据
MultiProvider 组合多个 Provider

3.4 Riverpod:下一代状态管理

Riverpod 无 BuildContext 依赖,支持编译期类型安全、易于测试和复用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 1. 定义 Provider
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
return CounterNotifier();
});

class CounterNotifier extends StateNotifier<int> {
CounterNotifier() : super(0);
void increment() => state++;
}

// 2. 在 runApp 外包一层 ProviderScope
void main() {
runApp(ProviderScope(child: MyApp()));
}

// 3. 在组件中使用(无需 context)
class CounterPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return ElevatedButton(
onPressed: () => ref.read(counterProvider.notifier).increment(),
child: Text('$count'),
);
}
}

ref 的三大方法

方法 作用
ref.watch() 监听变化,值变化时重建
ref.read() 一次性读取,不监听
ref.listen() 监听变化并执行副作用,不重建

3.5 Bloc:事件驱动架构

Bloc 将 UI 与业务逻辑解耦,通过 Event → Bloc → State 的流程管理状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 1. 定义 Event 和 State
abstract class CounterEvent {}
class Increment extends CounterEvent {}
class Decrement extends CounterEvent {}

class CounterState {
final int count;
CounterState(this.count);
}

// 2. 实现 Bloc
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterState(0)) {
on<Increment>((event, emit) => emit(CounterState(state.count + 1)));
on<Decrement>((event, emit) => emit(CounterState(state.count - 1)));
}
}

// 3. 在 UI 中使用
BlocProvider(
create: (_) => CounterBloc(),
child: BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Text('${state.count}');
},
),
)

3.6 Flutter 状态管理原理浅析

setState 与 Element 树

1
2
3
4
5
6
7
8
9
10
setState() 被调用


标记 Element 为 dirty


下一帧 SchedulerBinding 触发 build


Element.rebuild() → State.build()

InheritedWidget 与依赖收集

InheritedWidget 通过 context.dependOnInheritedWidgetOfExactType<T>() 建立「依赖关系」。当 InheritedWidget 更新时,依赖它的 Element 会被标记为脏并重建。

Provider 的 notifyListeners() 会触发 InheritedWidget 的更新,从而通知所有 context.watch 的消费者。


四、React vs Flutter 状态管理对比

4.1 概念映射

概念 React Flutter
本地状态 useState setState
复杂本地状态 useReducer 自建 StatefulWidget + 内部逻辑
跨层级共享 Context InheritedWidget / Provider
全局 Store Redux / Zustand Provider / Riverpod / Bloc
选择器/按需订阅 useSelector / useStore(selector) context.select / ref.watch(provider.select())

4.2 设计哲学差异

维度 React Flutter
更新粒度 组件级,虚拟 DOM diff Widget 树重建,Element 复用
数据流 单向(Redux)或自由(Zustand) 多为单向,Bloc 强调事件流
依赖注入 通过 props / Context 通过 context / ref(Riverpod)
服务端状态 React Query / SWR 等 Riverpod 的 FutureProvider、flutter_bloc 等

五、源码层面的理解

5.1 React useState 的调度

React 的 setState 会调用 dispatchSetState,将更新放入 updateQueue,由调度器(Scheduler)在合适的时机批量处理,触发 rendercommit

1
2
3
4
5
6
// 简化流程
setState(newState)
enqueueUpdate(fiber, update)
scheduleUpdateOnFiber(fiber)
→ performConcurrentWorkOnRoot / performSyncWorkOnRoot
→ commitRoot

5.2 Flutter ChangeNotifier 与 Listenable

ChangeNotifier 继承 Listenable,内部维护 _listeners 列表。notifyListeners() 遍历并调用所有监听者。

1
2
3
4
5
6
// 简化逻辑
void notifyListeners() {
for (final listener in _listeners) {
listener(); // 触发 Consumer 等重建
}
}

ProviderInheritedProvideraddListenerChangeNotifier,当 notifyListeners 被调用时,触发自身 updateShouldNotify 并重建子树。


六、实际项目应用案例

6.1 案例一:电商购物车(React + Zustand)

需求:跨页面购物车数量、增删改、持久化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// store/cartStore.js
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

export const useCartStore = create(
persist(
(set) => ({
items: [],
addItem: (product, qty = 1) =>
set((state) => ({
items: state.items.some((i) => i.id === product.id)
? state.items.map((i) =>
i.id === product.id ? { ...i, qty: i.qty + qty } : i
)
: [...state.items, { ...product, qty }],
})),
removeItem: (id) =>
set((state) => ({ items: state.items.filter((i) => i.id !== id) })),
totalCount: (state) => state.items.reduce((sum, i) => sum + i.qty, 0),
}),
{ name: 'cart-storage' }
)
);

// Header 中只订阅 totalCount,避免整 store 变化导致重渲染
const totalCount = useCartStore((s) =>
s.items.reduce((sum, i) => sum + i.qty, 0)
);

6.2 案例二:用户认证流(Flutter + Riverpod)

需求:登录态、token 刷新、路由守卫。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// providers/auth_provider.dart
final authStateProvider = StateNotifierProvider<AuthNotifier, AsyncValue<User?>>((ref) {
return AuthNotifier(ref);
});

class AuthNotifier extends StateNotifier<AsyncValue<User?>> {
AuthNotifier(this.ref) : super(const AsyncValue.loading()) {
_init();
}
final Ref ref;

Future<void> _init() async {
final token = await storage.getToken();
if (token == null) {
state = const AsyncValue.data(null);
return;
}
state = const AsyncValue.loading();
state = await AsyncValue.guard(() => api.getCurrentUser());
}

Future<void> login(String email, String pwd) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() => api.login(email, pwd));
}

void logout() {
storage.clearToken();
state = const AsyncValue.data(null);
}
}

// 路由守卫:根据 authState 跳转登录页或首页
ref.listen(authStateProvider, (prev, next) {
next.whenData((user) {
if (user == null) navigator.pushReplacement(LoginRoute());
});
});

6.3 案例三:列表筛选与分页(React + Redux Toolkit + RTK Query)

需求:筛选条件、分页、缓存、乐观更新。

1
2
3
4
5
6
7
8
// 使用 RTK Query 管理服务端状态
const { data, isLoading, refetch } = useGetProductsQuery({
page: currentPage,
category: selectedCategory,
});

// 本地筛选状态用 Redux 或 useState 均可
const [filters, setFilters] = useState({ category: '', sort: 'default' });

6.4 案例四:主题与多语言(Flutter + Provider)

需求:亮/暗主题、中英文切换,全局生效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 使用 MultiProvider 组合
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => ThemeModel()),
ChangeNotifierProvider(create: (_) => LocaleModel()),
],
child: MyApp(),
),
);

// 任意子组件
final theme = context.watch<ThemeModel>();
final locale = context.watch<LocaleModel>();

七、选型建议

场景 React 推荐 Flutter 推荐
小项目/原型 useState + Context setState + Provider
中大型项目 Redux Toolkit / Zustand Riverpod / Bloc
强类型、可测试 Redux + TypeScript / Zustand Riverpod
复杂业务流、事件驱动 Redux / XState Bloc
服务端状态 React Query / SWR Riverpod FutureProvider / dio + 自封装

八、总结

  • React:从 useState 起步,全局状态优先考虑 Redux ToolkitZustand,服务端状态用 React Query 等。
  • Flutter:从 setState 起步,共享状态用 Provider 入门,进阶用 RiverpodBloc
  • 选型时关注:团队熟悉度项目规模可测试性与框架生态的契合度

由浅入深掌握上述方案后,可以根据具体业务灵活组合,构建可维护、可扩展的状态管理体系。

iOS 开发中的 React Native

由浅入深,从基本概念到源码解析,带你全面掌握 React Native 在 iOS 平台的开发与应用


一、什么是 React Native?为什么选择它?

1.1 从 Hybrid 到 React Native

移动开发经历了从纯原生(Native)到 Hybrid(WebView)再到跨平台框架的演进:

方案 代表 优势 劣势
原生 Swift/ObjC 性能最佳、体验最好 双端重复开发
Hybrid Cordova、WebView 一套 HTML/JS 性能差、体验割裂
跨平台 React Native、Flutter 一套代码、接近原生 学习曲线、生态依赖

React Native (RN) 由 Meta 于 2015 年开源,核心理念是:用 JavaScript 编写逻辑,用原生组件渲染 UI,而不是在 WebView 中渲染。

1
2
传统 Hybrid:    JS/HTML → WebView 渲染 → 间接调用原生 API
React Native: JS/React → 虚拟 DOM → 原生组件(UILabel、UIView 等)直接渲染

1.2 为什么 iOS 开发者要学 React Native?

  • 业务需要:公司采用 RN 做跨端,需要维护/扩展原生能力
  • 原生桥接:RN 依赖大量原生模块(相机、蓝牙、支付等),需要 iOS 侧配合开发
  • 性能优化:理解 RN 与原生通信机制,才能做性能调优和问题排查
  • 新架构:新架构大量使用 C++、JSI,与 iOS 底层结合更紧密

1.3 RN 与 Flutter 的简要对比

维度 React Native Flutter
语言 JavaScript/TypeScript Dart
渲染 原生组件 自绘引擎(Skia)
包体积 相对较小 相对较大
生态 依赖 React、npm 独立生态
与原生交互 通过 Bridge/JSI 通过 Platform Channel

二、核心概念与架构

2.1 三层架构概览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌─────────────────────────────────────────────────────────┐
│ JavaScript 层 │
│ React 组件、业务逻辑、状态管理、事件处理 │
└──────────────────────────┬──────────────────────────────┘
│ Bridge / JSI
┌──────────────────────────▼──────────────────────────────┐
│ C++ 层(新架构) │
│ JSI、Fabric 渲染、TurboModules 调度 │
└──────────────────────────┬──────────────────────────────┘
│ FFI / Objective-C++
┌──────────────────────────▼──────────────────────────────┐
│ Native 层(iOS) │
│ UIKit、系统 API、自定义原生模块 │
└─────────────────────────────────────────────────────────┘

2.2 关键概念

概念 说明
Bridge 旧架构中 JS 与 Native 的异步通信桥梁,数据需序列化
JSI JavaScript Interface,新架构中 JS 可直接持有 C++ 对象引用,同步调用
Fabric 新架构的渲染系统,将布局、绘制逻辑下沉到 C++
TurboModules 新架构的原生模块系统,懒加载、类型安全
Hermes 字节码引擎,替代 JavaScriptCore,提升启动与运行性能

2.3 旧架构 vs 新架构

维度 旧架构 新架构
通信 Bridge 异步、JSON 序列化 JSI 同步、直接引用
渲染 各平台各自实现 Fabric 统一 C++ 渲染管线
原生模块 Native Modules 启动时全量加载 TurboModules 按需懒加载
类型 无强类型约定 通过 Codegen 生成类型

三、环境搭建与项目创建

3.1 环境要求

  • Node.js:建议 LTS 版本(18+)
  • Xcode:最新稳定版
  • CocoaPodsgem install cocoapods
  • Watchman(可选):brew install watchman,用于文件监听

3.2 创建新项目

1
2
3
4
5
6
7
8
9
10
11
# 使用 React Native CLI
npx @react-native-community/cli init MyApp

# 进入 iOS 目录
cd MyApp/ios

# 安装 CocoaPods 依赖
pod install

# 返回根目录,启动 Metro
cd .. && npx react-native start

另开终端运行 iOS:

1
2
3
npx react-native run-ios
# 或指定模拟器
npx react-native run-ios --simulator="iPhone 15"

3.3 项目结构(iOS 侧)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
MyApp/
├── ios/
│ ├── MyApp/ # 原生 iOS 工程
│ │ ├── AppDelegate.mm # 入口,加载 RN 根视图
│ │ ├── Info.plist
│ │ └── ...
│ ├── Podfile # CocoaPods 配置
│ ├── Podfile.lock
│ └── MyApp.xcworkspace # 用此打开工程
├── android/
├── src/ # JS 源码
├── node_modules/
├── package.json
└── metro.config.js

3.4 AppDelegate 与 RN 加载

典型的 AppDelegate.mm 中加载 RN 的流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];

RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"MyApp"
initialProperties:nil
launchOptions:launchOptions];

self.window.rootViewController = [[UIViewController alloc] init];
self.window.rootViewController.view = rootView;
[self.window makeKeyAndVisible];
return YES;
}

RCTRootView 负责加载 JS Bundle、创建 Bridge、挂载 React 组件树。


四、JS 与 Native 通信原理

4.1 旧架构:Bridge 模型

旧架构下,JS 与 Native 通过 异步 Bridge 通信:

1
2
3
4
5
6
7
JS 层发起调用

├─ 将参数序列化为 JSON

├─ 通过 Bridge 发送到 Native 队列

└─ Native 解析 JSON,执行对应模块方法,再序列化结果回传 JS

特点

  • 异步:所有跨端调用都是异步的
  • 序列化:参数和返回值需要 JSON 序列化,有性能开销
  • 全量加载:所有 Native Modules 在启动时注册

4.2 旧架构模块注册流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 1. 实现 RCTBridgeModule 协议
@interface MyNativeModule : NSObject <RCTBridgeModule>
@end

@implementation MyNativeModule

RCT_EXPORT_MODULE(); // 导出模块名,默认类名

RCT_EXPORT_METHOD(getDeviceId:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject) {
NSString *id = [[UIDevice currentDevice] identifierForVendor].UUIDString;
resolve(id);
}

@end

JS 端调用:

1
2
3
4
import { NativeModules } from 'react-native';
const { MyNativeModule } = NativeModules;

const id = await MyNativeModule.getDeviceId();

4.3 新架构:JSI 直接调用

JSI 允许 JavaScript 直接持有 C++ 对象的引用,无需经过 Bridge 序列化:

1
2
3
4
5
6
7
8
// C++ 侧:通过 JSI 暴露方法
jsi::Function getDeviceId = jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "getDeviceId"),
0,
[](jsi::Runtime& rt, const jsi::Value&, const jsi::Value*, size_t) {
return jsi::String::createFromUtf8(rt, getNativeDeviceId());
});

JS 可直接同步调用,无需 Promise 包装。


五、新架构:JSI、Fabric、TurboModules

5.1 JSI(JavaScript Interface)

JSI 是 C++ 实现的薄封装层,让 JS 引擎(Hermes/JSC)能够:

  • 调用 C++ 函数
  • 读取/写入 C++ 对象属性
  • 在 C++ 中执行 JS 回调
1
2
3
4
5
6
7
8
9
// 简化示意:HostObject 暴露给 JS 的对象
class DeviceModule : public jsi::HostObject {
jsi::Value get(jsi::Runtime& rt, const jsi::PropNameID& name) override {
if (name.utf8(rt) == "getDeviceId") {
return jsi::Function::createFromHostFunction(...);
}
return jsi::Value::undefined();
}
};

5.2 Fabric 渲染管线

Fabric 将 React 的渲染逻辑从各平台分别实现,统一到 C++:

1
2
3
4
5
6
7
8
9
10
11
12
13
React 组件树


Shadow Tree(C++ 中的布局树)


布局计算(Yoga)


提交到原生层(Mount)


iOS UIView 创建/更新

优势:减少跨 Bridge 的序列化、支持同步布局、更好的并发与优先级调度。

5.3 TurboModules

TurboModules 的特性:

  • 懒加载:只在首次被 JS 引用时初始化
  • 类型安全:通过 Codegen 从 TypeScript 定义生成 C++ 和 ObjC 代码
  • 同步能力:通过 JSI 可实现同步调用

定义原生模块的规范(新架构):

1
2
3
4
5
6
7
8
9
10
// NativeMyModule.ts (Codegen 规范)
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
getDeviceId(): Promise<string>;
multiply(a: number, b: number): number;
}

export default TurboModuleRegistry.getEnforcing<Spec>('MyNativeModule');

六、原生模块开发(Native Modules)

6.1 旧架构:RCT_EXPORT_MODULE

完整示例:实现一个获取设备信息的原生模块。

Objective-C:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// DeviceInfoModule.h
#import <React/RCTBridgeModule.h>

@interface DeviceInfoModule : NSObject <RCTBridgeModule>
@end

// DeviceInfoModule.m
#import "DeviceInfoModule.h"
#import <React/RCTLog.h>
#import <UIKit/UIKit.h>

@implementation DeviceInfoModule

RCT_EXPORT_MODULE(DeviceInfo)

RCT_EXPORT_METHOD(getDeviceInfo:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject) {
dispatch_async(dispatch_get_main_queue(), ^{
NSDictionary *info = @{
@"model": [[UIDevice currentDevice] model],
@"systemVersion": [[UIDevice currentDevice] systemVersion],
@"name": [[UIDevice currentDevice] name],
};
resolve(info);
});
}

@end

JavaScript:

1
2
3
4
5
6
7
8
9
10
11
12
import { NativeModules } from 'react-native';

const { DeviceInfo } = NativeModules;

async function loadDeviceInfo() {
try {
const info = await DeviceInfo.getDeviceInfo();
console.log(info);
} catch (e) {
console.error(e);
}
}

6.2 新架构:TurboModule + Swift

新架构推荐用 Swift 实现业务逻辑,用 ObjC++ 做 JSI 胶水层。

Swift 实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// DeviceInfoModule.swift
import Foundation

@objc(DeviceInfoModule)
class DeviceInfoModule: NSObject {
@objc
static func requiresMainQueueSetup() -> Bool {
return false
}

@objc
func getDeviceInfo(_ resolve: @escaping RCTPromiseResolveBlock,
reject: @escaping RCTPromiseRejectBlock) {
let info: [String: Any] = [
"model": UIDevice.current.model,
"systemVersion": UIDevice.current.systemVersion
]
resolve(info)
}
}

通过 RCT_EXTERN_MODULE 导出给 ObjC:

1
2
3
4
5
6
7
8
9
// DeviceInfoModule.m(桥接)
#import <React/RCTBridgeModule.h>

@interface RCT_EXTERN_MODULE(DeviceInfoModule, NSObject)

RCT_EXTERN_METHOD(getDeviceInfo:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)

@end

6.3 事件发送:从 Native 到 JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 在原生模块中
#import <React/RCTEventEmitter.h>

@interface MyModule : RCTEventEmitter <RCTBridgeModule>
@end

@implementation MyModule

RCT_EXPORT_MODULE()

- (NSArray<NSString *> *)supportedEvents {
return @[@"onScanResult"];
}

- (void)sendScanResult:(NSString *)result {
[self sendEventWithName:@"onScanResult" body:@{@"result": result}];
}

@end
1
2
3
4
5
6
import { NativeEventEmitter, NativeModules } from 'react-native';

const emitter = new NativeEventEmitter(NativeModules.MyModule);
emitter.addListener('onScanResult', (event) => {
console.log(event.result);
});

七、原生 UI 组件(Native UI Components)

7.1 使用 ViewManager 封装 UIView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// MyCustomViewManager.h
#import <React/RCTViewManager.h>

@interface MyCustomViewManager : RCTViewManager
@end

// MyCustomViewManager.m
#import "MyCustomViewManager.h"
#import "MyCustomView.h"

@implementation MyCustomViewManager

RCT_EXPORT_MODULE(MyCustomView)

- (UIView *)view {
return [[MyCustomView alloc] init];
}

RCT_EXPORT_VIEW_PROPERTY(title, NSString)
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)

@end
1
2
3
4
5
// MyCustomView.h
@interface MyCustomView : UIView
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) RCTBubblingEventBlock onPress;
@end

7.2 JS 侧使用

1
2
3
4
5
6
7
8
9
10
11
12
import { requireNativeComponent } from 'react-native';

const MyCustomView = requireNativeComponent('MyCustomView');

export default function Screen() {
return (
<MyCustomView
title="Hello"
onPress={(e) => console.log(e.nativeEvent)}
/>
);
}

7.3 新架构:Fabric 组件

新架构下,通过 Codegen 定义 Props 和事件,生成 C++ 与各平台代码,实现类型安全和更好的性能。


八、源码解析

8.1 初始化流程(iOS)

1
2
3
4
5
6
7
8
9
10
11
12
main()
└─ UIApplicationMain
└─ AppDelegate didFinishLaunchingWithOptions
└─ RCTRootView initWithBundleURL:moduleName:...
├─ 创建 RCTBridge
│ ├─ 加载 JavaScript Bundle
│ ├─ 初始化 JS 引擎(Hermes/JSC)
│ ├─ 注册所有 Native Modules
│ └─ 执行 JS 入口(AppRegistry.runApplication)

└─ 创建 RCTRootContentView
└─ 挂载 React 根组件,触发首次渲染

8.2 Bridge 核心结构(旧架构)

1
2
3
4
5
6
7
8
9
10
11
12
// 简化示意
@interface RCTBridge : NSObject
@property (nonatomic, strong) RCTBridge *batchedBridge; // 实际执行 Bridge
@end

// 模块调用流程
// JS: NativeModules.DeviceInfo.getDeviceInfo()
// -> __callFunction(moduleName, methodName, args)
// -> 序列化为 JSON,通过 RCTBridge 发送
// -> Native: RCTModuleData 根据 moduleName 找到模块实例
// -> 反序列化参数,invoke 对应方法
// -> 结果序列化回传 JS

8.3 新架构关键路径

  • JSIReactCommon/jsi/,提供 jsi::RuntimeHostObject
  • FabricReactCommon/react/renderer/,Shadow Tree、Mount、Component 定义
  • TurboModulesReactCommon/react/nativemodule/,模块注册与调用

可参考官方仓库:
https://github.com/facebook/react-native


九、实战示例

9.1 调用系统分享

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ShareModule.m
RCT_EXPORT_METHOD(share:(NSDictionary *)options
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject) {
dispatch_async(dispatch_get_main_queue(), ^{
NSString *title = options[@"title"] ?: @"";
NSString *url = options[@"url"] ?: @"";

UIActivityViewController *activityVC = [[UIActivityViewController alloc]
initWithActivityItems:@[title, [NSURL URLWithString:url]]
applicationActivities:nil];

UIViewController *rootVC = [UIApplication sharedApplication]
.keyWindow.rootViewController;
[rootVC presentViewController:activityVC animated:YES completion:nil];
resolve(@YES);
});
}

9.2 封装原生 TabBar

在 RN 中嵌入 UITabBarController 的容器,通过 Native Module 控制 Tab 切换,实现与原生 TabBar 一致的外观和动效。

9.3 列表性能优化:FlashList

1
2
3
4
5
6
7
8
9
10
11
12
13
import { FlashList } from '@shopify/flash-list';

function ProductList({ data }) {
const renderItem = ({ item }) => <ProductCard item={item} />;

return (
<FlashList
data={data}
renderItem={renderItem}
estimatedItemSize={100}
/>
);
}

FlashList 使用按需渲染和复用,比 FlatList 更适合长列表场景。


十、实际项目中的应用案例

10.1 电商 App:商品详情混合栈

  • Native:顶部 Banner 轮播、视频播放、复杂动效
  • RN:评价列表、推荐列表、加购/下单逻辑

通过 RCTRootView 嵌入到 UIViewController 的指定区域,实现「上原生、下 RN」的混合页面。

10.2 金融 App:安全键盘

输入密码时使用 Native 自定义键盘(避免 RN 侧键盘被截屏/录屏),通过 Native Module 将输入结果回传 JS:

1
2
3
4
5
6
7
8
RCT_EXPORT_METHOD(showSecureKeyboard:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject) {
SecureKeyboardViewController *vc = [[SecureKeyboardViewController alloc]
initWithCompletion:^(NSString *pin) {
resolve(pin);
}];
[self presentVC:vc];
}

10.3 地图与 LBS

地图、路径规划、定位等使用 Native SDK,通过 Native UI Component 和 Native Module 暴露给 RN,兼顾性能与功能完整性。

10.4 OTA 热更新

将打包好的 JS Bundle 下发到本地,启动时优先加载本地 Bundle,实现不发版即可更新业务逻辑(需注意各应用市场的合规要求)。


十一、常见问题与最佳实践

11.1 主线程与 UI 更新

原生模块中涉及 UI 的操作必须回到主线程:

1
2
3
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:vc animated:YES completion:nil];
});

11.2 避免内存泄漏

  • 使用 RCTEventEmitter 时正确实现 invalidate
  • Block 中使用 weakSelf 避免循环引用
  • 大对象及时释放,避免长期持有

11.3 调试技巧

1
2
3
4
5
6
7
8
# 查看 Metro 日志
npx react-native start

# 真机调试
npx react-native run-ios --device

# 查看原生日志
# Xcode -> Debug -> Open System Log,或使用 Console.app

11.4 性能建议

  • 长列表使用 FlashList 或优化 FlatListgetItemLayout
  • 复杂动画考虑 react-native-reanimated
  • 新项目尽量启用新架构(Fabric + TurboModules)
  • 图片使用 FastImage 或自定义 Native 图片组件做缓存

11.5 启用新架构

ios/Podfile 中:

1
ENV['RCT_NEW_ARCH_ENABLED'] = '1'

执行 pod install 后重新编译。


十二、总结

场景 建议
新项目 启用新架构(Fabric + TurboModules + Hermes)
原生能力扩展 通过 Native Module 暴露,优先用 Swift + ObjC 桥接
复杂 UI 封装 Native UI Component,或使用成熟第三方组件
性能敏感 长列表用 FlashList,动画用 Reanimated
调试 Metro + Flipper + Xcode 结合使用

React Native 在 iOS 上的核心价值是:用 React 生态统一业务逻辑,用原生能力保证体验与性能。理解 Bridge/JSI、Fabric、TurboModules 的演进,有助于在混合栈项目中做出更合适的架构与实现选择。