Flutter 插件lib_x的使用_lib_x 提供了一些预先配置的解决方案和简化方法,以更好地进行架构设计
Flutter 插件lib_x的使用_lib_x是一个包含一些经过良好测试且常用包的简单库。在此基础上,它提供了一些预先配置的解决方案和简化方法,以更好地进行架构设计
lib_x
是一个包含一些经过良好测试且常用包的简单库。在此基础上,它提供了一些预先配置的解决方案和简化方法,以更好地进行架构设计。
lib_x
旨在补充两件事:
- 面向对象的Dart特性,通过在Flutter应用中分离视图和数据关注点。
- 简化Flutter框架的方法,使我们能够更符合Flutter的方式进行开发。
此库的目的:
- 将一些基本包组合在一起。
- 跳过一些常用的样板代码。
- 提供简单的解决方案来处理路由、状态管理和数据提供者。
- 帮助设计模式和关注点分离。
- 语义化和自解释命名。
库中包含的包:
安装lib_x
后,你可以直接导入这些包,并使用以下解决方案之一。
解决方案
MaterialX & X 控制器
我们经常需要的功能不仅仅是push()
和pop()
,例如,我们需要通过URL导航、深度链接、不依赖于context
的路由等。为此,我们需要通过MaterialApp.router()
或MaterialX()
配置Navigator 2.0,并使用一个控制器类X
来分离和封装所有路由和主题管理的关注点。
MaterialX
它接受两个命名参数并构建一个MaterialApp.router()
,用于在runApp()
函数中使用:
MaterialApp materialApp: 包含主题和本地化关注点的MaterialApp。
RouteMap routeMap: 一个路由模式及其对应`Scaffold`的映射。
例如:
// lib/main.dart
void main() {
runApp(const MyApp());
}
// lib/src/views/const/route_map.dart
const String LoginPath = '/login';
const String RootPath = '/';
const String UserPath = '/user/';
const String ContactMePath = '/contactMe/';
const String PostPath = '/post/';
// if authentication, guarded routes
bool isLoggedIn = true;
final RouteMap routeMap = RouteMap(
routes: {
LoginPath: (info) => const MaterialPage(child: LoginPage()), // 无保护
RootPath: (info) => isLoggedIn // 有保护
? const MaterialPage(child: HomePage())
: const Redirect(LoginPath),
UserPath + ':username': (RouteData info) => isLoggedIn
? MaterialPage(child: UserPage(username: info.pathParameters['username']!))
: const Redirect(LoginPath),
UserPath + ':username' + ContactMePath : (RouteData info) => isLoggedIn
? MaterialPage(child: ContactMePage(username: info.pathParameters['username']!))
: const Redirect(LoginPath),
PostPath + ':postId': (RouteData info) => isLoggedIn
? MaterialPage(child: PostPage(postId: info.pathParameters['postId']!))
: const Redirect(LoginPath),
PostPath + ':postId/:commentId': (RouteData info) => isLoggedIn
? MaterialPage(
child: CommentPage(
postId: info.pathParameters['postId']!,
commentId: info.pathParameters['commentId']!,
))
: const Redirect(LoginPath),
// ... 其他路径
},
onUnknownRoute: (_) => const MaterialPage(child: NotFoundPage()), // 默认路由
);
// lib/src/views/const/material_app.dart
final MaterialApp materialApp = MaterialApp(
title: 'Example App',
debugShowCheckedModeBanner: false,
theme: myLightTheme,
darkTheme: myDarkTheme,
themeMode: ThemeMode.system,
);
// lib/src/views/my_app.dart
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialX(
materialApp: materialApp,
routeMap: routeMap,
);
}
}
X 控制器
它是MaterialX
小部件的抽象控制器类,具有以下接口:
ThemeData X.theme: 返回当前上下文的ThemeData对象。
MediaQueryData X.mediaQuery: 返回当前上下文的MediaQueryData对象。
ValueController<ThemeMode> X.themeMode: 是MaterialX的主题模式控制器。如果提供了亮色和暗色主题,则MaterialX将自动更改系统主题模式的变化,除非你指定了一个与`ThemeMode.system`不同的值。
void X.switchTheme({ThemeMode? to}): 它接受这些值`[ThemeMode.system, ThemeMode.dark, ThemeMode.light]`,并更新`X.themeMode`的值。它还会相应地更新状态栏的颜色和亮度。如果你没有传递主题模式的值,它将只是切换当前主题模式到相反的状态,并停止监听系统主题模式的变化。要再次更改主题与系统一致,使用`X.switchTheme(to: ThemeMode.system);`
void X.setStatusBar({Color? color, Brightness? brightness}): 设置状态栏的颜色和亮度。
void X.forcePortrait(): 强制纵向方向。
void X.forceLandscape(): 强制横向方向。
void X.allowAutoOrientation(): 允许纵向和横向方向。
ScaffoldX
在完成MyApp
之后,我们需要创建包含Scaffold
小部件并对应于routeMap
中定义路径的页面。ScaffoldX
是一种快速组合带有默认配置的Scaffold
的方式。它有以下命名参数:
Widget ScaffoldX({
required Widget body,
Color? bgColor,
DecorationImage? bgDecorationImage,
Widget? appBar,
double appBarHeight = 60,
Widget? drawer,
Widget? bottomNavigationBar,
Widget? bottomSheet,
Widget? fab, // FloatingActionButton
FloatingActionButtonLocation? fabLocation,
BoxConstraints? constraints,
TextStyle textStyle = const TextStyle(color: black, fontSize: 16),
VoidCallback? onInit,
bool safeArea = true,
bool scrollView = false,
})
例如:
class UserPage extends StatelessWidget {
final String username;
const UserPage({Key? key, required this.username}) : super(key: key);
@override
Widget build(BuildContext context) {
return ScaffoldX(
onInit: () => debugPrint(username),
appBar: const MyAppBar(),
bottomNavigationBar: const MyNavBar(),
bgDecorationImage: const DecorationImage(
image: AssetImage('assets/images/bg.png'),
fit: BoxFit.fill,
opacity: .3,
),
body: Center(
child: Text('Hello $username'),
),
);
}
}
注意:如果不使用ScaffoldX
,确保实现BackButtonInterceptor
来处理返回手势。
数据提供者
我们不再需要通过参数从父组件传递数据到子组件,这很快会变得混乱。我们可以使用DataProvider
来分配所需的数据给一个组件,只有其后代组件可以访问该数据。
数据提供者小部件
class UserProvider extends DataProvider<UserModel> {
final UserModel userModel;
const UserProvider({
super.key,
required this.userModel, // 第一个参数:需要声明的数据对象
required super.child, // 第二个参数:需要并传递子组件给超类
}) : super(data: userModel); // 将数据传递给超类
static UserProvider of(BuildContext context) =>
context.dependOnInheritedWidgetOfExactType<UserProvider>()!;
}
StatefulData & ReBuilder
StatefulWidget
在许多情况下非常有用,但在数据管理方面可能不太适用。在实际应用中,我们需要将数据层与渲染层解耦,将每一层分开。这就是为什么这种解决方案分为两个单独的类:视图类Widget
和StatefulData
控制器类。
StatefulData
它是一个扩展了ChangeNotifier
的更好命名的类。它是ReBuilder
小部件的控制器类。这个类应该将所有的数据逻辑与视图逻辑分离。当数据发生变化时,并且调用了@protected update()
方法时,ReBuilder
小部件将重建以反映数据变化。
ReBuilder
它是一个从上下文中抽象出来的AnimatedBuilder
。当StatefulData
的状态改变时,它会重新构建。它有两个命名参数:
StatefulData controller: StatefulData对象的一个实例。
Function builder: 返回一个Widget的函数,当控制器需要时,它将重新构建。
例如:
class UserModel extends StatefulData {
final String id;
String username;
UserModel({required this.id, required this.username});
void changeUsername(String newName) {
username = newName;
update(); // 触发Rebuilder渲染更改
}
}
class ProfileWidget extends StatelessWidget {
const ProfileWidget({super.key});
@override
Widget build(BuildContext context) {
final UserModel userModel = UserProvider.of(context).userModel;
return ReBuilder(
controller: userModel,
builder: () {
return Text(userModel.username);
}
);
}
}
ValueController & ReactiveBuilder
有时我们只需要监听一个独立值的状态。我们将创建两个单独的类:视图类和值控制器类。
ValueController<T>
它基于ValueNotifier
构建的值控制器对象,具有以下公共接口:
value: 返回当前值。
ValueListenable listenable: 可用于`ValueListenableBuilder`的可监听对象。
void update(T v): 更新类型为T的值属性。
void set onChange(VoidCallback callback): 如果你想附加一个回调函数,每当值改变时运行。
void dispose(): 调用此方法后,valueController不能被使用。
ReactiveBuilder
它是从上下文中抽象出来的ValueListenableBuilder
。当controller.update(value)
被调用时,它会重新构建。它有两个命名参数:
ValueController<T>
Function builder(T value): 带值参数的函数,返回一个将在控制器值改变时重新构建的小部件。
例如:
final ValueController<ThemeMode> themeModeController = ValueController<ThemeMode>(ThemeMode.dark);
class SwitchThemeButton extends StatelessWidget {
const SwitchThemeButton({super.key});
@override
Widget build(BuildContext context) {
ThemeMode value() => themeModeController.value == ThemeMode.dark
? ThemeMode.light
: ThemeMode.dark;
return TextButton(
onPressed: () => themeModeController.update(value()),
child: const Text('Change Theme'),
);
}
}
class AdaptiveText extends StatelessWidget {
final String text;
final Color? lightModeC;
final Color? darkModeC;
final double? fontSize;
const AdaptiveText(
this.text, {
super.key,
this.lightModeC,
this.darkModeC,
this.fontSize,
});
@override
Widget build(BuildContext context) {
return ReactiveBuilder(
controller: themeModeController,
builder: (ThemeMode mode) => Text(
text,
style: TextStyle(
color: mode == ThemeMode.dark
? darkModeC ?? white
: lightModeC ?? black,
fontSize: fontSize,
),
textAlign: TextAlign.center,
),
);
}
}
XUtils
这是一个提供一些便捷快速解决方案的抽象类。它具有以下静态接口:
bool XUtils.isUrl(String string): 检查字符串是否为URL。
bool XUtils.isAsset(String path): 检查路径是否以"assets/"开头。
bool XUtils.isSVG(String path): 检查路径是否包含".svg"。
bool getter XUtils.isSysDarkMode: 如果系统处于暗模式则返回true,否则返回false。
ThemeMode getter XUtils.sysThemeMode: 返回系统的当前ThemeMode值。
int getter XUtils.now: 返回现在时间戳的整数值。
String XUtils.formatTimestamp(int timestamp, {bool shortMonthFormat = true}): 将时间戳转换为可读格式。
bool XUtils.isNumeric(String str): 检查字符串是否为数字。
bool XUtils.isEmail(String email): 根据HTML5电子邮件验证规范检查字符串是否为有效电子邮件格式。
String XUtils.genString({int length = 16}): 生成默认长度为16的随机字符串。
String XUtils.genNum({int length = 16}): 生成默认长度为16的随机数字字符串。
String XUtils.genId({int length = 16}): 生成基于时间戳的ID字符串。
Widgets
PersistStateWidget
如果你有一个滚动视图(如ScrollView),并且希望在导航到其他标签页时保持其滚动位置,可以使用PersistStateWidget
包装滚动视图。
例如:
class MyListView extends StatelessWidget {
const MyListView({super.key});
@override
Widget build(BuildContext context) {
final List<Widget> myList = List.generate(100, (index) => Text(index.toString())).toList();
// 包装ListView以维护其滚动状态
return PersistStateWidget(
child: ListView.builder(
itemBuilder: (context, index) => myList[index],
),
);
}
}
DismissModalWidget
如果你要推送一个模态对话框,并希望点击对话框外部时弹出,可以使用DismissModalWidget
包装对话框。这比showDialog()
中的barrierDismissible
更可靠。
例如:
class MyDialog extends StatelessWidget {
const MyDialog({super.key});
@override
Widget build(BuildContext context) {
return const Center(child: Text('Dialog'));
}
}
class ShowMyDialogButton extends StatelessWidget {
const ShowMyDialogButton({super.key});
@override
Widget build(BuildContext context) {
return TextButton(
onPressed: () => showDialog(
context: context,
barrierDismissible: true, // 不可靠
builder: (context) {
return const DismissableModal(child: MyDialog());
},
),
child: const Text('Show Dismissable Dialog'),
);
}
}
注意:如果你使用X.showModal(child: MyDialog())
而不是showDialog()
,则默认具有可取消行为。
结束语
这些是我个人的实现,不知道是否对其他人有用,但希望有所帮助。如果您有任何建议或优化,请随时提出GitHub上的pull请求。您也可以自由地扩展或修改它以适应您的需求。
感谢实现这些包的作者,当然还有Dart和Flutter的开发者。
祝编码愉快!
更多关于Flutter 插件lib_x的使用_lib_x 提供了一些预先配置的解决方案和简化方法,以更好地进行架构设计的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter 插件lib_x的使用_lib_x 提供了一些预先配置的解决方案和简化方法,以更好地进行架构设计的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,作为一个IT专家,我可以为你提供一个关于如何在Flutter项目中集成并使用一个假想的未知功能插件lib_x
的代码案例。请注意,由于lib_x
是一个假想的插件,其具体功能和API可能会有所不同,但以下代码案例展示了如何集成和使用一个Flutter插件的基本步骤。
首先,假设lib_x
插件已经发布在pub.dev
上,你可以通过修改pubspec.yaml
文件来添加这个依赖:
dependencies:
flutter:
sdk: flutter
lib_x: ^1.0.0 # 假设这是lib_x的最新版本号
然后,运行flutter pub get
来下载并安装这个插件。
接下来,在你的Flutter应用中,你可以按照以下方式使用lib_x
插件。以下是一个简单的例子,展示了如何初始化插件并调用其某个假设的功能(由于我们不知道lib_x
的具体功能,这里假设它有一个名为performAction
的方法):
import 'package:flutter/material.dart';
import 'package:lib_x/lib_x.dart'; // 导入lib_x插件
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
LibX? _libX;
@override
void initState() {
super.initState();
// 初始化lib_x插件
_libX = LibX();
// 调用lib_x的某个假设功能
_performAction();
}
Future<void> _performAction() async {
try {
// 假设performAction是一个返回Future的方法
var result = await _libX!.performAction();
// 处理结果
print('Action performed with result: $result');
} catch (e) {
// 处理错误
print('Error performing action: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Demo Home Page'),
),
body: Center(
child: Text('Waiting for lib_x action to complete...'),
),
);
}
}
在这个例子中,我们做了以下几件事:
- 在
pubspec.yaml
文件中添加了lib_x
作为依赖。 - 在
MyApp
类中创建了Flutter应用的基本结构。 - 在
MyHomePage
类中,通过initState
方法初始化了lib_x
插件,并调用了其假设的performAction
方法。 - 使用
Future
和async/await
来处理异步操作。
请注意,由于lib_x
是一个假想的插件,上述代码中的LibX
类和performAction
方法都是假设的。在实际使用中,你需要参考lib_x
插件的官方文档来了解其具体的API和使用方法。
希望这个代码案例能帮助你理解如何在Flutter项目中集成和使用一个未知功能的插件。如果你有更多关于lib_x
插件的具体信息或问题,欢迎继续提问!