Flutter预渲染页面插件prerender的使用

Flutter预渲染页面插件prerender的使用

Prerender小部件允许构建依赖于其他小部件大小的子树。

Flutter的渲染管道提供了父级大小信息,但在build方法中不允许计算其他小部件的大小。Prerender包通过在单个帧内测量一个小部件并延迟最终构建直到其尺寸已知来解决此问题。

入门指南

Prerender小部件通过以下几步解决其布局以访问目标的尺寸:

  1. 渲染对象将target作为Prerender的唯一子项进行布局,允许目标填充父项。
  2. 渲染对象存储计算出的尺寸,并调用builder函数,传递目标的解析尺寸。
  3. builder回调根据之前计算的布局决定最终的子项小部件树形状。

重新构建具有不同目标小部件的树将导致builder再次被调用,并传递更新的目标尺寸。

注意

由于`Prerender`试图解决的问题性质,它需要计算测量小部件的推测布局。这意味着运行推测布局的限制以及每次构建时额外布局计算的性能考虑。

使用方法

pubspec.yaml中添加依赖:

dependencies:
  prerender: ^1.0.0

将依赖于某些小部件布局的小部件树包装起来:

Prerender(
  // 提供您想要测量的小部件:
  target: ...,
  // 使用计算出的布局构建实际的子项:
  builder: (context, childSize, child) {
    ...
  },
)

例如,仅当有足够的水平空间时才在标题前添加头像:

Prerender(
  target: Title(),
  builder: (context, childSize, child) {
    return Row(
      children: [
        if (childSize.width < 200) Avatar(),
        child,
      ],
    );
  },
)

单帧渲染

除了对构建小部件有更细粒度的控制外,使用Prerender的另一个关键优势是在单个帧内访问和使用其他小部件的尺寸信息:

// 使用 GlobalKey(命令式)
final titleKey = GlobalKey();

Widget build(BuildContext context) {
  var titleSize = Size.zero;
  final renderObject = titleKey.currentContext?.findRenderObject();
  if (renderObject != null) {
    titleSize = (renderObject as RenderBox).size;
  }

  return Row(
    children: [
      // 大小始终落后一个帧。
      if (titleSize.width < 200) Avatar(),
      Title(key: titleKey),
    ],
  );
}

// 使用 Prerender(声明式)
Widget build(BuildContext context) {
  return Prerender(
    target: Title(),
    builder: (context, childSize, child) {
      return Row(
        children: [
          if (childSize.width < 200) Avatar(),
          child,
        ],
      );
    },
  );
}

示例

扩展文本

仅在文本宽度大于可用水平区域时显示展开按钮。

固定底部

如果内容小于可用区域,则将底部按钮固定到屏幕底部边缘。

响应式表单

在折叠状态下,将底部表单的高度与主体的垂直尺寸对齐,以防止显示空白区域。

完整示例代码

import 'package:example/examples/expanding_text.dart';
import 'package:example/examples/responsive_sheet.dart';
import 'package:example/examples/sticking_footer.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const ExamplesApp());
}

class ExamplesApp extends StatefulWidget {
  const ExamplesApp({super.key});

  [@override](/user/override)
  State<ExamplesApp> createState() => _ExamplesAppState();
}

class _ExamplesAppState extends State<ExamplesApp> {
  final items = [
    ('扩展文本', const ExpandingTextExample()),
    ('固定底部', const StickingFooterExample()),
    ('响应式表单', const ResponsiveSheetExample()),
  ];

  late var currentItem = items.last;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          centerTitle: false,
          title: Text(currentItem.$1),
        ),
        drawer: Drawer(
          child: Column(
            children: [
              for (var (item) in items)
                DrawerItem(
                  title: item.$1,
                  onSelect: () {
                    setState(() => currentItem = item);
                  },
                ),
            ],
          ),
        ),
        body: currentItem.$2,
      ),
    );
  }
}

class DrawerItem extends StatelessWidget {
  const DrawerItem({
    super.key,
    required this.title,
    required this.onSelect,
  });

  final String title;
  final VoidCallback onSelect;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(title),
      onTap: () {
        onSelect();
        Scaffold.of(context).closeDrawer();
      },
    );
  }
}

更多关于Flutter预渲染页面插件prerender的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter预渲染页面插件prerender的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,预渲染页面可以提升应用的启动速度和用户体验,尤其是在需要加载复杂内容或网络请求的场景。prerender 并不是一个官方的Flutter插件,但你可以通过自定义的方式实现页面预渲染。以下是一个基本的步骤和示例,展示如何在Flutter中实现页面预渲染。

1. 创建一个自定义的预渲染页面

你可以使用 FutureBuilderStreamBuilder 来实现页面的预渲染。在这里,我们将使用 FutureBuilder 来模拟一个异步加载数据并预渲染页面的过程。

import 'package:flutter/material.dart';

class PreRenderedPage extends StatelessWidget {
  final Future<String> futureData;

  PreRenderedPage({required this.futureData});

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<String>(
      future: futureData,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return Center(child: CircularProgressIndicator());
        } else if (snapshot.hasError) {
          return Center(child: Text('Error: ${snapshot.error}'));
        } else {
          return Scaffold(
            appBar: AppBar(title: Text('Pre-Rendered Page')),
            body: Center(child: Text('Data: ${snapshot.data}')),
          );
        }
      },
    );
  }
}

2. 在应用启动时预渲染页面

你可以在应用启动时预加载数据,并将预渲染的页面缓存起来,以便在需要时快速显示。

import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 预加载数据
  final futureData = fetchData();

  runApp(MyApp(futureData: futureData));
}

Future<String> fetchData() async {
  // 模拟一个异步网络请求
  await Future.delayed(Duration(seconds: 2));
  return 'Hello, Pre-Rendered World!';
}

class MyApp extends StatelessWidget {
  final Future<String> futureData;

  MyApp({required this.futureData});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: PreRenderedPage(futureData: futureData),
    );
  }
}

3. 在需要时显示预渲染的页面

当你需要显示预渲染的页面时,可以直接导航到该页面,而无需等待数据加载。

Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => PreRenderedPage(futureData: futureData),
  ),
);

4. 使用 PageStorageProvider 进行状态管理

为了进一步提升性能,你可以使用 PageStorage 或状态管理工具(如 Provider)来缓存页面的状态,以避免重复加载数据。

5. 使用 SizedBoxPlaceholder 优化用户体验

在数据加载过程中,你可以使用 SizedBoxPlaceholder 来占位,以避免页面布局发生抖动。

if (snapshot.connectionState == ConnectionState.waiting) {
  return Center(child: CircularProgressIndicator());
}
回到顶部