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

如何在 flutter 中显示一个空视图,Widget.build 不能返回 null 来表示没有什么可以呈现。

106584 次浏览
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
);
}
}

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

SizedBox(
width: 200.0,
height: 300.0,
)

对于任何像我一样想知道什么是“正确的方式”来显示一个空的小部件-官方资料代码库使用这个:

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

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

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

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

有很多可能的解决方案,比如

  1. Widget build(context) => SizedBox();
    
  2.  Widget build(context) => Container();
    
  3.  Widget build(context) => Scaffold();
    

我的问题非常相似,但是我发现 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'),)

根据你的需要。

有关初始化器列表的更多信息,请参见: 省道构造函数后冒号

Column中我使用的是 SizedBox(height: 0.01)

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

这可能为时已晚,但所有这些解决方案都不适用于某些场景,如使用 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!
)

通过这种方式,我们可以有大量的 null 和 UI 不会受到任何空 Widget的影响。

例子 PopupMenuButton:

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) {}
)




表演

容器 = 166,173毫秒

缩小 = 164,523 ms

DIY

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.elapsedMilliseconds}ms');
});
}