react-navigation

https://reactnavigation.org/docs/

@react-navigation/native、@react-navigation/native-stack跟react-navigation的区别?

react-navigation是早期的版本,@react-navigation是新的架构,采用组件化的设计,每种导航器都是一个单独的包

@react-navigation/stack
@react-navigation/elements
@react-navigation/bottom-tabs
@react-navigation/native-stack
@react-navigation/native
@react-navigation/drawer
@react-navigation/routers
@react-navigation/material-top-tabs
@react-navigation/devtools
@react-navigation/compat

1
2
3
4
5
# 安装
npm install --save @react-navigation/stack



Read More

RN常用的开源库

antd-mobile-rn

antd-mobile-rn是一个基于React Native的UI组件库,由蚂蚁金服团队开发和维护。包含了许多常用的UI组件,如按钮、列表、输入框、导航栏、轮播图、对话框等。这些组件都遵循了Ant Design的设计规范,具有一致的视觉样式和交互行为。

async-retry

async-retry是一个用于在异步操作失败时进行重试的JavaScript库。它提供了一个简单的API,你可以使用它来包装一个异步函数,当这个函数抛出错误时,async-retry会自动重试这个函数。

Read More

form_bloc

form_bloc是结合bloc的表单库,运用bloc的特性实现UI和逻辑的分离,使得表单的逻辑更加清晰,代码更加简洁。

基本用法

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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
import 'package:flutter/material.dart';
import 'package:flutter_form_bloc/flutter_form_bloc.dart';

void main() => runApp(const App());

class App extends StatelessWidget {
const App({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: LoginForm(),
);
}
}

class LoginFormBloc extends FormBloc<String, String> {
// 不同的表单项对应不同的FieldBloc
final email = TextFieldBloc(
validators: [
FieldBlocValidators.required,
FieldBlocValidators.email,
],
);

final password = TextFieldBloc(
validators: [
FieldBlocValidators.required,
],
);

final showSuccessResponse = BooleanFieldBloc();

LoginFormBloc() {
//添加表单项
addFieldBlocs(
fieldBlocs: [
email,
password,
showSuccessResponse,
],
);
}

/// 表单提交方法
@override
void onSubmitting() async {
debugPrint(email.value);
debugPrint(password.value);
debugPrint(showSuccessResponse.value.toString());

await Future<void>.delayed(const Duration(seconds: 1));

// 提交进度变化
emitSubmitting(progress: 0.2);
await Future<void>.delayed(Duration(milliseconds: 400));
emitSubmitting(progress: 0.6);
await Future<void>.delayed(Duration(milliseconds: 400));
emitSubmitting(progress: 1.0);
//取消提交
//emitSubmissionCancelled();

if (showSuccessResponse.value) {
emitSuccess(); //提交成功,发送成功消息,触发UI更新
} else {
emitFailure(failureResponse: 'This is an awesome error!'); //提交失败,发送失败消息,触发UI更新
}
}

@override
Future<void> close() {
// 释放所有表单项
email.close();
password.close();
showSuccessResponse.close();
return super.close();
}

}

class LoginForm extends StatelessWidget {
const LoginForm({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => LoginFormBloc(),
child: Builder(
builder: (context) {
final loginFormBloc = context.read<LoginFormBloc>();

return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(title: const Text('Login')),
body: FormBlocListener<LoginFormBloc, String, String>(
onSubmitting: (context, state) {
LoadingDialog.show(context);
},
onSubmissionFailed: (context, state) {
LoadingDialog.hide(context);
},
onSuccess: (context, state) {
LoadingDialog.hide(context);

Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => const SuccessScreen()));
},
onFailure: (context, state) {
LoadingDialog.hide(context);

ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.failureResponse!)));
},
child: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
child: AutofillGroup(
child: Column(
children: <Widget>[
TextFieldBlocBuilder(
textFieldBloc: loginFormBloc.email,
keyboardType: TextInputType.emailAddress,
autofillHints: const [
AutofillHints.username,
],
decoration: const InputDecoration(
labelText: 'Email',
prefixIcon: Icon(Icons.email),
),
),
TextFieldBlocBuilder(
textFieldBloc: loginFormBloc.password,
suffixButton: SuffixButton.obscureText,
autofillHints: const [AutofillHints.password],
decoration: const InputDecoration(
labelText: 'Password',
prefixIcon: Icon(Icons.lock),
),
),
SizedBox(
width: 250,
child: CheckboxFieldBlocBuilder(
booleanFieldBloc: loginFormBloc.showSuccessResponse,
body: Container(
alignment: Alignment.centerLeft,
child: const Text('Show success response'),
),
),
),
ElevatedButton(
onPressed: loginFormBloc.submit,
child: const Text('LOGIN'),
),
],
),
),
),
),
);
},
),
);
}
}

class LoadingDialog extends StatelessWidget {
static void show(BuildContext context, {Key? key}) => showDialog<void>(
context: context,
useRootNavigator: false,
barrierDismissible: false,
builder: (_) => LoadingDialog(key: key),
).then((_) => FocusScope.of(context).requestFocus(FocusNode()));

static void hide(BuildContext context) => Navigator.pop(context);

const LoadingDialog({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Center(
child: Card(
child: Container(
width: 80,
height: 80,
padding: const EdgeInsets.all(12.0),
child: const CircularProgressIndicator(),
),
),
),
);
}
}

class SuccessScreen extends StatelessWidget {
const SuccessScreen({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Icon(Icons.tag_faces, size: 100),
const SizedBox(height: 10),
const Text(
'Success',
style: TextStyle(fontSize: 54, color: Colors.black),
textAlign: TextAlign.center,
),
const SizedBox(height: 10),
ElevatedButton.icon(
onPressed: () => Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => const LoginForm())),
icon: const Icon(Icons.replay),
label: const Text('AGAIN'),
),
],
),
),
);
}
}


表单项类型

Read More