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: 跟踪BuildContextRenderBox的全局位置。

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.&lt;fn&gt;             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.&lt;fn&gt;                                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.&lt;fn&gt;                                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.&lt;fn&gt;             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.&lt;fn&gt;                                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

1 回复

更多关于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());
      }
回到顶部