【问题标题】: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 Screen 和 Second 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 感谢您的帮助,我正在添加代码示例并尝试更好地解释我的问题。

标签: flutter dart flutter-web


【解决方案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
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-04
  • 1970-01-01
  • 1970-01-01
  • 2012-08-02
  • 1970-01-01
  • 2013-12-13
相关资源
最近更新 更多