nameless
nameless

Reputation: 2375

How to present an empty view in flutter?

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

Answers (10)

Egor
Egor

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

Rohinton Collins
Rohinton Collins

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

CopsOnRoad
CopsOnRoad

Reputation: 268054

There are many possible solutions of it. Like

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

Upvotes: 43

Mamrezo
Mamrezo

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

Aawaz Gyawali
Aawaz Gyawali

Reputation: 3364

The recommended widget to show nothing is to use SizedBox.

SizedBox(
  width: 200.0,
  height: 300.0,
)

Upvotes: 8

Roddy R
Roddy R

Reputation: 1470

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

Upvotes: 35

mahfuz
mahfuz

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

Luke
Luke

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

TheDrev
TheDrev

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

Rahul Mahadik
Rahul Mahadik

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

Related Questions