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

How to reuse the same layout screen without creating a new widget tree branch

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

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

我已经尝试创建一个新的 Scaffold class 并向每个屏幕添加 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!'),
        ),
      ),
    );
  }
}

我会尽力更好地解释这个问题。

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

我还尝试更改代码以创建新的 MyScaffold 并重复使用相同的 Widget class :

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 时,它会刷新页面并重建完整视图。

目前在 Flutter web 上最有效的方法是使用 TabController with a TabBarView in a Stateful widget with SingleTickerProviderStateMixin.

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

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!'),
    );
  }
}