Reputation: 2375
How to present an empty view in flutter as Widget.build cannot return null to indicate that there is nothing to render.
Upvotes: 211
Views: 159526
Reputation: 10344
For anyone like me who was wondering what is "the correct way" to show an empty widget - official Material codebase uses this:
Widget build(BuildContext context) {
return const SizedBox.shrink();
}
SizedBox.shrink()
is a widget that is unlike Container
or Material
has no background or any decorations whatsoever. It sizes itself to the smallest area possible, if not influenced by parent constraints.
Upvotes: 443
Reputation: 76
If this is in a list of children, then how about removing the null entries in the list?
return Column(
children: <Widget?>[
Text(heading),
_detailOrNull(context), // function returns a Widget?
].whereType<Widget>().toList()
);
Upvotes: 1
Reputation: 268054
There are many possible solutions of it. Like
Widget build(context) => SizedBox();
Widget build(context) => Container();
Widget build(context) => Scaffold();
Upvotes: 43
Reputation: 1499
This may be too late but all of these solutions are not suitable in some scenarios like playing with PopupMenuItems
or affecting UI rendering!
Null safety Update
[
.
.
.
if(condition)...[//Conditionally widget(s) here
Something(...),
],
.
.
.
],
The solution is to remove null items before pass to the rendering component:
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!
)
in this way, we can have a lot of null and UI will not affect by any empty Widget
.
PopupMenuButton
example where we cannot pass 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) {}
)
And this can be used as API with extension
:
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) {}
)
Upvotes: 15
Reputation: 3364
The recommended widget to show nothing is to use SizedBox
.
SizedBox(
width: 200.0,
height: 300.0,
)
Upvotes: 8
Reputation: 1470
Container = 166,173 ms
SizedBox.shrink = 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');
});
}
Upvotes: 35
Reputation: 3223
Inside Column
I am using SizedBox(height: 0.01)
Column(
children: [
Text('Title'),
name == ''
? SizedBox(height: 0.01) // show nothing
: Text(name) // show name
]
)
Upvotes: 2
Reputation: 37
My problem was very similar, but I found Container
and SizedBox.shrink
still affect the UI (frustratingly).
The best solution I've got is to build it differently using a named constructor and initializer list. An example of these would be:
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));
}
}
}
And to use it:
Center(child: MyWidget.withoutThing(name: 'Foo'),)//don't show thing
or
Center(child: MyWidget(name: 'Foo'),)
based on your needs.
For more on initializer lists: Colon after Constructor in dart
Upvotes: 2
Reputation: 489
When a build function returns null, the error message from flutter is :
Build functions must never return null. To return an empty space that causes the building widget to fill available room, return "new Container()". To return an empty space that takes as little room as possible, return "new Container(width: 0.0, height: 0.0)".
Upvotes: 11
Reputation: 12291
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(
);
}
}
You can also simply return an empty Container
and avoid using the Scaffold
entirely. But this would result in a black screen if this is the only primary widget in you app, you can set the color
property of the Container
if you want to prevent the black background.
Example:
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white // This is optional
);
}
}
Upvotes: 31