Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What is the best way to optionally include a widget in a list of children #3783

Closed
drewwarren opened this issue May 6, 2016 · 19 comments
Closed

Comments

@drewwarren
Copy link

drewwarren commented May 6, 2016

I often find myself optionally wanting a child in a column component. For example let's say I have a Column with a title, body, and optional footer. What is the best way to achieve this?

Option 1: explicitly build the children, omitting the optional child

List<Widget> buildChildren() {
  var builder = [
    new Title(),
    new Body()
  ];
  if (shouldShowFooter) {
    builder.add(new Footer());
  }
  return builder;
}

Widget build() {
  return new Column(children: buildChildren());
}

Option 2: ternary operator using an empty widget when the optional child should not be shown

Widget build() {
  return new Column(
    children: [
      new Title(),
      new Body(),
      shouldShowFooter ? new Footer() : new Container(width: 0, height: 0)
    ]
  );
}

Option 3...: You show me!

I have been using option 2 but referencing a final Widget emptyWidget = new Container(width: 0, height: 0) to make it more readable.

What approach would you suggest?

@drewwarren
Copy link
Author

This is a re-post from flutter-users. One suggestion there was to allow null in child lists to represent this case.

@Hixie
Copy link
Contributor

Hixie commented May 6, 2016

Option 1 is the currently optimal path.

Supporting nulls in child lists seems reasonable. It actually might work already, since we use nulls internally to represent children that have been removed by GlobalKey regrafting.

@drewwarren
Copy link
Author

drewwarren commented May 6, 2016

assert(!children.any((Widget child) => child == null));
prevents null in slow mode.

The following exception is thrown with --no-checked

I/flutter : ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter : The following NoSuchMethodError was thrown building DefaultTextStyle(inherit: false; color:
I/flutter : Color(0xdd000000); size: 14.0; weight: 400; baseline: alphabetic):
I/flutter : The null object does not have a getter 'key'.
I/flutter : NoSuchMethodError: method not found: 'key'
I/flutter : Receiver: null
I/flutter : Arguments: []
I/flutter : When the exception was thrown, this was the stack:
I/flutter : #0      Object._noSuchMethod (dart:core-patch/object_patch.dart:42)
I/flutter : #1      Object.noSuchMethod (dart:core-patch/object_patch.dart:45)
I/flutter : #2      Element.inflateWidget (package:flutter/src/widgets/framework.dart:1069)
I/flutter : #3      MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:2233)
I/flutter : #4      Element.inflateWidget (package:flutter/src/widgets/framework.dart:1083)
I/flutter : #5      Element.updateChild (package:flutter/src/widgets/framework.dart:964)
I/flutter : #6      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:1506)
I/flutter : #7      BuildableElement.rebuild (package:flutter/src/widgets/framework.dart:1405)
I/flutter : #8      ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:1469)
I/flutter : #9      ComponentElement.mount (package:flutter/src/widgets/framework.dart:1464)
I/flutter : #10     Element.inflateWidget (package:flutter/src/widgets/framework.dart:1083)
...

@drewwarren
Copy link
Author

With this question I'm less concerned about efficiency and more concerned with readability.

@Hixie
Copy link
Contributor

Hixie commented May 6, 2016

Ok, looks like we have to add null checks in more places then.

@abarth
Copy link
Contributor

abarth commented May 7, 2016

@yjbanov has a proposal that would make the moral equivalent of this code work:

Widget build() {
  return new Column(
    children: [
      new Title(),
      new Body(),
      if (shouldShowFooter) { new Footer() }
    ]
  );
}

However, that would require changes to the language, which means it's a bit further out than allowing nulls.

@Hixie Hixie modified the milestone: Flutter 1.0 May 20, 2016
@Hixie
Copy link
Contributor

Hixie commented Jan 29, 2017

I think we should just go with option 1 for now. You can use where to allow nulls if you want:

bool notNull(Object o) => o != null;
Widget build() {
  return new Column(
    children: <Widget>[
      new Title(),
      new Body(),
      shouldShowFooter ? new Footer() : null
    ].where(notNull).toList(),
  );
}

@pavel-ismailov
Copy link

pavel-ismailov commented Jul 11, 2018

Issue is still here and I have to use method from my BaseState class

  @protected
  widgets(List<Widget> widgets) {
    return widgets..removeWhere((widget) => widget == null);
  }

and then in the code

    Column(
      children: widgets ([
        widget1,
        widget2,
        (a != null) ? Text(...) : null    
      ]),
    )

Do you have plans add support nulls in child lists?

@zoechi
Copy link
Contributor

zoechi commented Jul 11, 2018

@pavel-ismailov you might want to upvote #17862

@Zhuinden
Copy link

Zhuinden commented Jan 2, 2019

#3783 (comment) In Kotlin you'd just do something like

fun build(): Widget =
  Column(
    children: buildArray {
        add(Title())
        add(Body())
        if(shouldShowFooter) add(Footer())
    }
  )

where your buildArray is receiving a method like ArrayBuilder.() -> Unit which at the end returns the new array, or so.

And crazier DSLs overrode the unary plus to replace add(Title()) with +Title() but I personally think that is overkill.

@yjbanov
Copy link
Contributor

yjbanov commented Jan 3, 2019

@Zhuinden you might be interested in checking out this proposal: https://github.com/dart-lang/language/blob/master/accepted/future-releases/control-flow-collections/feature-specification.md. It supports it more directly, and it also composes well with other proposals, such as https://github.com/dart-lang/language/blob/master/working/0125-static-immutability/feature-specification.md.

@juliusspencer
Copy link

Check out the Visibility widget. Just found it after reading all this. :)

@ivnsch
Copy link
Contributor

ivnsch commented Mar 2, 2019

Concerning null support (though this is a bit out of topic here as it's not about lists), it makes sense for example when using a FutureBuilder and wanting to return "nothing" when the state is none or waiting (if you expect the future to be almost immediate).

@elMuso
Copy link

elMuso commented Jun 26, 2019

Is there a performance difference between doing this

bool notNull(Object o) => o != null;
Widget build() {
  return new Column(
    children: <Widget>[
      new Title(),
      new Body(),
      shouldShowFooter ? new Footer() : null
    ].where(notNull).toList(),
  );
}

and doing something like this ?

void addIfNonNull(Widget child, List children) {
  if (child != null) {
    children.add(child);
  }
}
//build method
@override
  Widget build(BuildContext context) {
    final children = <Widget>[
      Title(),
    ];
    addIfNonNull(Body(), children);
    addIfNonNull(Footer(), children);

    return Column(children: children);
  }
}

I mean, the simplest solution is short

  addIfNonNull(Body(), children);
    addIfNonNull(Footer(), children);

could be replaced by

if(someCondition!=null){children.add(Body());}
if(someCondition!=null){children.add(Footer());}

The problem i find here is that everytime the build method is called, is gonna rebuild everything , in every solution. Maybe we should have an "empty" widget that works like a null , that way we can include the second option and instead of calling Container(),or SizedBox() , we could call the "null" widget ex : WNull()?

@natebosch
Copy link
Contributor

The canonical way to write this as long as your Dart SDK is at least 2.3.0 is:

Widget build() {
  return Column(
    children: <Widget>[
      Title(),
      Body(),
      if(shouldShowFooter) Footer(),
    ],
  );
}

@elMuso
Copy link

elMuso commented Jun 26, 2019

The canonical way to write this as long as your Dart SDK is at least 2.3.0 is:

Widget build() {
  return Column(
    children: <Widget>[
      Title(),
      Body(),
      if(shouldShowFooter) Footer(),
    ],
  );
}

:O didn't know that was possible !!!!!!!!!!! Flutter ftw! :D i love you xd <3

@julianoappelklein
Copy link

julianoappelklein commented Oct 3, 2019

Yeah, but what if I don't want to use the beta channels and want to keep everything inline?

How about using yield?

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: (() sync* {
            yield Text( 'You have pushed the button this many times:' );
            yield Text('$_counter', style: Theme.of(context).textTheme.display1);
            if(this._counter>5){ yield FlatButton(child: Text('Go For It'), onPressed: ()=> launch('https://www.google.com')); }
          })().toList(),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), 
    );
  }

It works for me, but I don't have any idea of how much that affects performance.

@drewwarren - can this be your option 3?

@daysond
Copy link

daysond commented Dec 4, 2020

The canonical way to write this as long as your Dart SDK is at least 2.3.0 is:

Widget build() {
  return Column(
    children: <Widget>[
      Title(),
      Body(),
      if(shouldShowFooter) Footer(),
    ],
  );
}

:O didn't know that was possible !!!!!!!!!!! Flutter ftw! :D i love you xd <3

This really worked and I think it's the best way.

@github-actions
Copy link

github-actions bot commented Aug 9, 2021

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 9, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests