如何在颤动中呈现一个空视图?

Posted

技术标签:

【中文标题】如何在颤动中呈现一个空视图?【英文标题】:How to present an empty view in flutter? 【发布时间】:2019-04-26 13:52:14 【问题描述】:

如何在 Flutter 中呈现一个空视图,因为 Widget.build 无法返回 null 以指示没有要渲染的内容。

【问题讨论】:

所以返回一个空的 ContainterMaterial 或类似的小部件.. 如果您需要占位符,请不要忘记有一个占位符小部件:youtube.com/watch?v=LPe56fezmoo 【参考方案1】:

对于像我这样想知道什么是显示空小部件的“正确方法”的人 - 官方 Material 代码库使用这个:

Widget build(BuildContext context) 
  return SizedBox.shrink();

SizedBox.shrink() 是一个与ContainerMaterial 不同的小部件,它没有任何背景或任何装饰。如果不受父约束的影响,它将自身调整到尽可能小的区域。

【讨论】:

基本上只返回SizedBox(width: 0, height: 0) 这是唯一不会在列表视图中引发错误的答案 尽管 SizedBox.shrink 很有用,但它仍然会在屏幕上占用一些空间,我宁愿说它就像 SizedBox(width: 5, height: 5) 如果您使用 Column and Row,这将不起作用,它仍将计为 col 和 row 项【参考方案2】:
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget 
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  


class MyHomePage extends StatefulWidget 
  @override
  _MyHomePageState createState() => _MyHomePageState();


class _MyHomePageState extends State<MyHomePage> 
  @override
  Widget build(BuildContext context) 
    return Scaffold(

    );
  

您也可以简单地返回一个空的Container 并完全避免使用Scaffold。但是如果这是您应用中唯一的主要小部件,这将导致黑屏,如果您想防止黑背景,您可以设置Containercolor 属性。

例子:

class _MyHomePageState extends State<MyHomePage> 
  @override
  Widget build(BuildContext context) 
    return Container(
      color: Colors.white // This is optional
    );
  

【讨论】:

【参考方案3】:

有很多可能的解决方案。喜欢

    Widget build(context) => Container();
    
    Widget build(context) => SizedBox();
    
    Widget build(context) => Scaffold();
    

【讨论】:

【参考方案4】:

性能

容器 = 166,173 毫秒

SizedBox.shrink = 164,523 毫秒

自己动手

main() async 
  testWidgets('test', (WidgetTester tester) async 
    await tester.pumpWidget( Container());
    final Stopwatch timer = new Stopwatch()..start();
    for (int index = 0; index < 5000000; index += 1) 
      await tester.pump();
    
    timer.stop();
    debugPrint('Time taken: $timer.elapsedMillisecondsms');
  );

【讨论】:

哇,好研究 如果您不知道该代码放在哪里,请按双档并打开widget_test.dart【参考方案5】:

当构建函数返回 null 时,flutter 的错误信息是:

构建函数绝不能返回 null。要返回导致建筑小部件填满可用空间的空白空间,请返回“new Container()”。要返回占用尽可能少空间的空白空间,请返回“new Container(width: 0.0, height: 0.0)”。

【讨论】:

是的。如果我在零安全方案中运行应用程序,我会遇到这个问题。所以,我选择听从你的建议。【参考方案6】:

这可能为时已晚,但所有这些解决方案都不适用于某些场景,例如玩 PopupMenuItems 或影响 UI 渲染!

空安全更新

  [
    .
    .
    .
    if(condition)...[//Conditionally widget(s) here
      Something(...),
    ],
    .
    .
    .
],

解决方案是在传递给渲染组件之前删除空项:

Column(
  children: [
    Text('Title'),
    name != '' 
      ? Text(name) //show name
      : null // just pass a null we will filter it in next line!
  ].where((e) => e != null).toList()// Filter list and make it List again!
)

这样,我们可以有很多空,UI不会被任何空的Widget影响。

PopupMenuButton 示例我们无法通过 SizedBox

PopupMenuButton(
    icon: Icon(Icons.add),
    itemBuilder: (context) => [
        PopupMenuItem(
            child: Row(children:[ Icon(Icons.folder), Text('So something')]),
            value: 'do.something',
        ),
        1 > 2 //⚠️ A false condition
        ? PopupMenuItem(
           child: Row(children: [ Icon(Icons.file_upload), Text('⚠️No way to display ?')]),
            'no.way.to.display',
          )
        : null,// ⚠️ Passing null
        PopupMenuItem(
           child: Row(children: [ Icon(Icons.file_upload), Text('Do something else')]),
            'do.something.else',
        )
    ].where((e) => e != null).toList(),//ℹ️ Removing null items  
    onSelected: (item) 
)

这可以用作extension的API:

extension NotNulls on List 
  ///Returns items that are not null, for UI Widgets/PopupMenuItems etc.
  notNulls() 
    return where((e) => e != null).toList();
  


//Usage:
PopupMenuButton(
    icon: Icon(Icons.add),
    itemBuilder: (context) => [
        PopupMenuItem(
            child: Row(children:[ Icon(Icons.folder), Text('So something')]),
            value: 'do.something',
        ),
        1 > 2 //⚠️ A false condition
        ? PopupMenuItem(
           child: Row(children: [ Icon(Icons.file_upload), Text('⚠️No way to display ?')]),
            'no.way.to.display',
          )
        : null,// ⚠️ Passing null
        PopupMenuItem(
           child: Row(children: [ Icon(Icons.file_upload), Text('Do something else')]),
            'do.something.else',
        )
    ].notNulls(),//ℹ️ Removing null items  
    onSelected: (item) 
)


【讨论】:

【参考方案7】:

不显示任何内容的推荐小部件是使用SizedBox

SizedBox(
  width: 200.0,
  height: 300.0,
)

【讨论】:

此建议的参考依据是什么? 我花了很长时间才回复,但 SizedBox 已在第一方小部件(如 AppBar)中用于显示空白内容。【参考方案8】:

我的问题非常相似,但我发现 ContainerSizedBox.shrink 仍然影响 UI(令人沮丧)。

我得到的最佳解决方案是使用命名构造函数和初始化列表以不同方式构建它。例如:

class MyWidget extends StatelessWidget 
    final String name = 'Default';
    final bool hasThing = true;

    MyWidget(this.name);

    MyWidget.withoutThing(this.name) : hasThing = false;

    @override
    Widget build(BuildContext context) 
        //define widgets they have in common here (as many as possible)
        if (hasThing) 
            return Widget(child: Thing(this.name));
         else 
            return Widget(child: WithoutThing(this.name));
        
    

并使用它:

Center(child: MyWidget.withoutThing(name: 'Foo'),)//don't show thing

Center(child: MyWidget(name: 'Foo'),)

根据您的需要。

有关初始化列表的更多信息:Colon after Constructor in dart

【讨论】:

【参考方案9】:

Column里面我用的是SizedBox(height: 0.01)

Column(
  children: [
    Text('Title'),
    name == ''
    ? SizedBox(height: 0.01) // show nothing
    : Text(name) // show name
  ]
)

【讨论】:

以上是关于如何在颤动中呈现一个空视图?的主要内容,如果未能解决你的问题,请参考以下文章

如何在颤动中动态设置根视图?

如何遍历地图列表并在颤动列表视图中显示文本小部件?

如何在颤动中居中图表视图或树视图?

如何在颤动中将格式从列表视图更改为网格视图?

如何在颤动的列表视图中设置卡片的宽度?

如何在颤动的列表视图中嵌套列表视图?