176

How to present an empty view in flutter as Widget.build cannot return null to indicate that there is nothing to render.

nameless
  • 2,015
  • 2
  • 11
  • 10
  • 8
    so return an empty `Containter` or `Material` or similar widget.. – pskink Nov 24 '18 at 06:21
  • 1
    In case you need a placeholder, do not forget there is a placeholder widget : https://www.youtube.com/watch?v=LPe56fezmoo – TheDrev Jul 22 '20 at 07:19

9 Answers9

397

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 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.

George Zvonov
  • 9,401
  • 5
  • 33
  • 37
36

There are many possible solutions of it. Like

  1. Widget build(context) => SizedBox(); 
    
  2.  Widget build(context) => Container();
    
  3.  Widget build(context) => Scaffold();
    
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
  • 1
    Returning as scaffold just to prevent returning `null` is not a good solution! Additionally, a container will fill its parent constraints, which can often be an undesired result. – jnt Dec 02 '22 at 11:30
  • @jnt I just listed different ways of doing it. The better one is `SizedBox` which is equivalent to `SizedBox.shrink()`. – CopsOnRoad Dec 02 '22 at 14:59
  • `SizedBox()` is technically not the same as `SizedBox.shrink()`, because `SizedBox()` will have its properties width and height set to null. However, I agree that it will have the same behaviour as `SizedBox.shrink()` if the box does not contain any element – Jack' May 10 '23 at 13:20
32

Performance

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');
  });
}
Roddy R
  • 1,222
  • 10
  • 10
31
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
    );
  }
}
bytesizedwizard
  • 5,529
  • 3
  • 17
  • 39
Rahul Mahadik
  • 11,668
  • 6
  • 41
  • 54
14

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


Mamrezo
  • 1,309
  • 16
  • 20
  • Someone should performance test this. I imagine filtering nulls out of a short list will perform better than size boxes. As such, this might be a better solution even when sizebox or empty container is considered "good enough". – SpiRail Oct 31 '22 at 18:42
10

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)".

TheDrev
  • 469
  • 2
  • 7
  • 16
  • Yes. If I run app in a null-safety regimen, I face this problem. So, I choose to follow your recommendation. – kokserek Jun 11 '21 at 06:08
8

The recommended widget to show nothing is to use SizedBox.

SizedBox(
  width: 200.0,
  height: 300.0,
)
Aawaz Gyawali
  • 3,244
  • 5
  • 28
  • 48
  • 17
    What's the reference for this recommendation? – zgosalvez Apr 10 '19 at 06:40
  • 1
    Took me ages to reply, but SizedBox has been used inside first party widgets like AppBar to show something empty. – Aawaz Gyawali Oct 08 '21 at 07:54
  • 1
    When creating a Container with width and height 0, Android Studio proposes to replace it with a SizedBox with width and height 0 in case you want to show nothing. – xuiqzy May 01 '23 at 01:01
2

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

Luke
  • 37
  • 5
  • What would the `Thing` and `WithoutThing` widgets look like? They're still widgets so they are not really empty and will still render. – Hans Vn Aug 17 '22 at 22:12
2

Inside Column I am using SizedBox(height: 0.01)

Column(
  children: [
    Text('Title'),
    name == ''
    ? SizedBox(height: 0.01) // show nothing
    : Text(name) // show name
  ]
)
mahfuz
  • 2,728
  • 20
  • 18
  • you are giving a very small size can't be check but still it will take the space. you can put 0.0 instead 0.01. the question was, how and what is the best best way to do the job. – Kapil Bansal Sep 27 '22 at 13:44