如何在不创建新的小部件树分支的情况下重用相同的布局屏幕

Posted

技术标签:

【中文标题】如何在不创建新的小部件树分支的情况下重用相同的布局屏幕【英文标题】:How to reuse the same layout screen without creating a new widget tree branch 【发布时间】:2020-10-19 15:11:03 【问题描述】:

我正在开发 Flutter Web 应用程序。

目标是为大部分路由屏幕重用相同的布局屏幕小部件(Drawer,AppBar)。

我已经尝试创建一个新的 Scaffold 类并将 body 小部件添加到每个屏幕。

问题是每次我导航到新屏幕时。在小部件树上创建了一个新的 (MyScaffold)。所以对性能不好。

我也尝试使用嵌套路由器,问题是嵌套路由器不受 url 支持,我无法通过输入 URL 导航到屏幕。

有没有其他合适的方法来处理这个问题。

谢谢

添加代码示例:

import 'package:flutter/material.dart';

void main() => runApp(AppWidget());

class AppWidget extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      initialRoute: '/',
      routes: 
        '/': (context) => FirstScreen(),
        '/second': (context) => SecondScreen(),
      ,
    );
  


class FirstScreen extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        title: Text('First Screen'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text('Launch screen'),
          onPressed: () 
            Navigator.pushReplacementNamed(context, '/second');
          ,
        ),
      ),
    );
  


class SecondScreen extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Screen"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () 
            Navigator.pushReplacementNamed(context, '/');
          ,
          child: Text('Go back!'),
        ),
      ),
    );
  


我会尝试更好地解释这个问题。

如您所见,First ScreenSecond Screen 具有完全相同的小部件树结构。但是每次flutter都会删除Screen Widget并创建一个新的。

我还尝试更改代码以创建一个新的 MyScaffold 并重用相同的 Widget 类:

class AppWidget extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      initialRoute: '/',
      routes: 
        '/': (context) => MyScallfold(
              bodyWidget: FirstScreen(),
            ),
        '/second': (context) => MyScallfold(
              bodyWidget: SecondScreen(),
            ),
      ,
    );
  


class FirstScreen extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return RaisedButton(
      onPressed: () 
        Navigator.pushReplacementNamed(context, '/second');
      ,
      child: Text('To Screen 2!'),
    );
  


class SecondScreen extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return RaisedButton(
      onPressed: () 
        Navigator.pushReplacementNamed(context, '/');
      ,
      child: Text('To Screen 1!'),
    );
  


class MyScallfold extends StatelessWidget 
  Widget bodyWidget;
  MyScallfold(this.bodyWidget);

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        title: Text('WebAppTest'),
      ),
      body: bodyWidget,
    );
  


我注意到每次使用导航时,树的所有小部件都会重新构建(renderObject #id 已更改)

那么是否可以在flutter中复用同一个RenderObject(AppBar、RichText)来优化性能?

【问题讨论】:

你在使用Navigator.of(context).push(newRoute)吗?如果是这样,请尝试使用Navigator.of(context).pushReplacement(newRoute) 否则,如果您使用相关代码扩展您的问题,这将非常有帮助。这样可以更轻松地为您提供帮助。 @josxha 感谢您的帮助,我正在添加代码示例并尝试更好地解释我的问题。 【参考方案1】:

快速回答是否定的,反正还没有。目前,当您使用 Navigator 时,它会刷新页面并重建完整视图。

目前在 Flutter web 上最有效的方法是在带有 SingleTickerProviderStateMixin 的 Stateful 小部件中使用 TabController 和 TabBarView。

它只加载屏幕上的小部件,但不需要重新加载页面来查看其他页面。您的示例将如下所示(我添加了动画以过渡到下一页,但您可以将其删除):

import 'package:flutter/material.dart';

TabController tabController;

class MainScreen extends StatefulWidget 
  @override
  _MainScreenState createState() => _MainScreenState();


class _MainScreenState extends State<MainScreen> with SingleTickerProviderStateMixin 
  int activeTab = 0;
  @override
  void initState() 
    tabController = TabController(length: 3, vsync: this, initialIndex: 0)
      ..addListener(() 
        setState(() 
          activeTab = tabController.index;
        );
      );
    super.initState();
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        title: Text('WebAppTest'),
      ),
      body: Expanded(
        child: TabBarView(
          controller: tabController,
          children: <Widget>[
            FirstScreen(), //Index 0
            SecondScreen(), //Index 1
            ThirdScreen(), //Index 2
          ],
        ),
      ),
    );
  


class FirstScreen extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return RaisedButton(
      onPressed: () 
        tabController.animateTo(2);
      ,
      child: Text('To Screen 3!'),
    );
  


class SecondScreen extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return RaisedButton(
      onPressed: () 
        tabController.animateTo(0);
      ,
      child: Text('To Screen 1!'),
    );
  


class ThirdScreen extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return RaisedButton(
      onPressed: () 
        tabController.animateTo(1);
      ,
      child: Text('To Screen 2!'),
    );
  

【讨论】:

感谢您的回复。 TabView 可能是 Flutter 中部分重新加载屏幕的更好解决方案。我在考虑 Navigator,因为我需要 Flutter Web 中的 Url 路由页面。因此,替代解决方案可能会将 TabViewController 索引作为路由器的 参数 传递,并使用导航器导航到确切的页面。 是的,这也许是解决方案,应该可以正常工作。您还可以将带有单词的 Url 映射到索引。 /page1 == 0, /page2 == 1

以上是关于如何在不创建新的小部件树分支的情况下重用相同的布局屏幕的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Flutter 的小部件树中将新的 MaterialPageRoute 作为子项打开

具有单独布局的嵌套 Qt 小部件

展开并在行中展开时,如何在不更改布局的情况下包装小部件列表?

如何在不影响 Pyqt5 中的小部件的情况下将背景图像添加到主窗口

如何将旧的小部件迁移到新的小部件

QT - 在不阻止他的 GUI 的情况下显示小部件