Flutter插件noob的使用方法介绍
Flutter插件noob的使用方法介绍
noob
一些有用的工具用于Flutter应用程序。
UriRouter
一个简单的基于Uri
的页面路由。
class BooksPage extends StatelessWidget {
static final route = UriRoute(
path: '/books/:id',
pageBuilder: (context, params) => BooksPage(id: params.pathParams['id']!),
);
static String path(String id) => route.build(pathParams: {'id': id});
...
}
final router = UriRouter(routes: [
BooksPage.route,
]);
runApp(MaterialApp(onGenerateRoute: router.generateRoute));
Navigator.pushNamed(context, BooksPage.path('42'));
Hooks
一些有用的钩子:
useDisposable
: 管理需要被处置的对象。useRebuild
: 手动触发HookWidget
/HookBuilder
的重建。useVariable
: 轻量级钩子,创建一个不触发重建的可变值变量。useListener
/useValueListener
: 附加回调到Listenable
/ValueListenable
,而不会在通知时触发重建。useAsyncValue
: 将Future
转换为AsyncValue
。useLastValidAsyncData
: 记住最近有效的AsyncData
。
Providers
一些有用的提供者:
lastPointerEventProvider
: 跟踪活跃指针的最后PointerEvent
。globalPositionProvider
: 跟踪BuildContext
的RenderBox
的全局位置。
PointerIndicator
PointerIndicator(child: ...)
PointerIndicator
显示PointerEvent
的位置,因此允许记录包括“手指”的屏幕。
BuildTracker
void main() {
// 初始化 `TrackingBuildOwnerWidgetsFlutterBinding` 以启用跟踪
TrackingBuildOwnerWidgetsFlutterBinding.ensureInitialized();
// 初始化 `BuildTracker`
final tracker = BuildTracker(printBuildFrameIncludeRebuildDirtyWidget: false);
// 每10秒打印导致重建的前10个堆栈
Timer.periodic(const Duration(seconds: 10), (_) => tracker.printTopScheduleBuildForStacks());
// 运行
runApp(...);
}
许多人花费数小时试图找出为什么Flutter会不断重建他们的应用程序中的数十个小部件。为了帮助解决这个问题,BuildTracker
可以跟踪每个帧中哪些小部件被重建,更重要的是,是什么导致了这些重建。
Flutter的渲染管道通常处于空闲状态,直到树中的某个小部件标记为脏,例如通过调用State
对象上的setState
方法。在这种情况下,Flutter将调度该小部件在下一帧中进行构建。然而,在下一帧实际构建之前,可能还有许多其他小部件被标记为脏。所有这些小部件我们称之为后续帧的“构建根”。
当Flutter最终构建下一帧时,它从构建根开始,并且这些根可能会触发更多小部件的构建,因为这一过程是递归地向下遍历小部件树的。
对于每一帧,BuildTracker
会打印所有重建的小部件及其各自的构建根堆栈跟踪。
考虑以下示例测试用例:
void main() {
TrackingBuildOwnerAutomatedTestWidgetsFlutterBinding.ensureInitialized();
final tracker = BuildTracker(enabled: false);
testWidgets('Test build frames', (tester) async {
tracker.enabled = true;
final text = ValueNotifier('');
debugPrint('# `tester.pumpWidget(...)`');
debugPrint('');
await tester.pumpWidget(
ValueListenableBuilder<String>(
valueListenable: text,
builder: (_, value, child) => Directionality(
textDirection: TextDirection.ltr,
child: Text(value),
),
),
);
debugPrint("# Looping `text.value`");
debugPrint('');
for (var i = 0; i < 10; i++) {
text.value = '$i';
await tester.pump();
}
debugPrint('# test end');
debugPrint('');
tracker.enabled = false;
tracker.printTopScheduleBuildForStacks();
});
}
如果我们运行这个例子,BuildTracker
会生成包含重建和脏小部件的Markdown格式的日志。完整的输出可以在这里找到。
例如,在text.value = '0'
之后,我们有:
重建的小部件
[root]
ValueListenableBuilder<String> ← [root]
Directionality ← ValueListenableBuilder<String> ← [root]
Text ← Directionality ← ValueListenableBuilder<String> ← [root]
脏的小部件(构建根)
[root]
堆栈跟踪 #1beada3:
Element.markNeedsBuild package:flutter/src/widgets/framework.dart 4157:12
RenderObjectToWidgetAdapter.attachToRenderTree package:flutter/src/widgets/binding.dart 1102:15
WidgetsBinding.attachRootWidget package:flutter/src/widgets/binding.dart 934:7
WidgetTester.pumpWidget.<fn> package:flutter_test/src/widget_tester.dart 520:15
_CustomZone.run dart:async
TestAsyncUtils.guard package:flutter_test/src/test_async_utils.dart 72:41
WidgetTester.pumpWidget package:flutter_test/src/widget_tester.dart 519:27
* main.<fn> test/build_tracker_test.dart 17:18
因此,我们可以轻松地看到test/build_tracker_test.dart 25:10
是触发帧的实际位置,即text.value = '0'
。
此外,调用BuildTracker.printTopScheduleBuildForStacks
会打印导致重建的前10个堆栈跟踪:
导致重建的前10个scheduleBuildFor
堆栈跟踪(构建根)
10次
堆栈跟踪 #16e7ece8:
State.setState package:flutter/src/widgets/framework.dart 1287:15
_ValueListenableBuilderState._valueChanged package:flutter/src/widgets/value_listenable_builder.dart 182:5
ChangeNotifier.notifyListeners package:flutter/src/foundation/change_notifier.dart 243:25
ValueNotifier.value= package:flutter/src/foundation/change_notifier.dart 309:5
* main.<fn> test/build_tracker_test.dart 30:12
...
1次
堆栈跟踪 #1beada3:
Element.markNeedsBuild package:flutter/src/widgets/framework.dart 4157:12
RenderObjectToWidgetAdapter.attachToRenderTree package:flutter/src/widgets/binding.dart 1102:15
WidgetsBinding.attachRootWidget package:flutter/src/widgets/binding.dart 934:7
WidgetTester.pumpWidget.<fn> package:flutter_test/src/widget_tester.dart 520:15
_CustomZone.run dart:async
TestAsyncUtils.guard package:flutter_test/src/test_async_utils.dart 72:41
WidgetTester.pumpWidget package:flutter_test/src/widget_tester.dart 519:27
* main.<fn> test/build_tracker_test.dart 17:18
...
PeriodicListenable
final timer30 = useDisposable<PeriodicListenable>(() => PeriodicListenable(const Duration(seconds: 30)), (_) => _.dispose());
useListener(timer60, callInitially: false, callback: () => context.refresh(daaProvider));
更多关于Flutter插件noob的使用方法介绍的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter插件noob的使用方法介绍的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在Flutter开发中,如果你遇到“功能未定义”的错误,通常是因为你尝试使用了一个未定义的方法、类或插件。针对你提到的“noob”插件,以下是一些可能的原因和解决方法:
1. 插件未安装或未导入
- 原因:你可能忘记在
pubspec.yaml
文件中添加插件依赖,或者忘记在代码中导入插件。 - 解决方法:
- 打开
pubspec.yaml
文件,确保你已经添加了noob
插件的依赖。例如:dependencies: flutter: sdk: flutter noob: ^1.0.0 # 确保版本号正确
- 然后运行
flutter pub get
来安装依赖。 - 在需要使用插件的Dart文件中,确保导入了插件:
import 'package:noob/noob.dart';
- 打开
2. 插件不存在或拼写错误
- 原因:你可能拼错了插件的名称,或者插件本身并不存在。
- 解决方法:
- 检查
pubspec.yaml
文件中的插件名称是否正确。 - 你可以在 pub.dev 上搜索插件,确认是否存在。
- 检查
3. 插件未正确初始化
- 原因:某些插件需要在应用启动时进行初始化,如果你没有初始化插件,可能会导致“功能未定义”的错误。
- 解决方法:
- 查看插件的文档,确保你已经按照要求进行了初始化。例如:
void main() { Noob.initialize(); // 假设插件需要初始化 runApp(MyApp()); }
- 查看插件的文档,确保你已经按照要求进行了初始化。例如: