skip to Main Content

You understand better when see the code and error.

I have a StatelessWidget names StateWidget like this:

class StateWidget extends StatelessWidget {
  const StateWidget({
    super.key,
    required this.readyWidget,
    this.error = false,
    this.errorWidget,
    this.loading = false,
    this.loadingWidget,
  });

  final Widget readyWidget;
  final bool error;
  final Widget? errorWidget;
  final bool loading;
  final Widget? loadingWidget;

  @override
  Widget build(BuildContext context) {
    if (loading) {
      return loadingWidget ?? LoadingCircularProgressIndicatorWidget();
    }

    if (error) {
      return errorWidget ?? ErrorIconWidget();
    }

    if (!loading && !error) return readyWidget;

    return Container();
  }
}

And I have a StatefulWidget named StateWidgetExampleView like this:

class StateWidgetExampleView extends StatefulWidget {
  const StateWidgetExampleView({super.key});

  @override
  State<StateWidgetExampleView> createState() => _StateWidgetExampleViewState();
}

class _StateWidgetExampleViewState extends State<StateWidgetExampleView> {
  bool loading = true;
  String? data;

  @override
  void initState() {
    super.initState();

    Future.delayed(Duration(milliseconds: 1000)).then((value) {
      data = "Gelen Veri!";
      setState(() {
        loading = false;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("StateWidgetExampleView"),
      ),
      body: StateWidget(
        loading: loading,
        error: data == null,
        readyWidget: Text(data!),
      ),
    );
  }
}

I get a sample data in initState function in 1 seconds. You guess that "loadingWidget appears because of loading is true". This idea is kind of true but it throws error like Null check operator used on a null value. I think this renders the all widgets, then checks the conditions. Let’s look at the error and wait 1 seconds then our StateWidget is ready. I want a structure that I can control error, loading and ready widgets. But it throws like this error. How can I build the structure that I want?

Error:
enter image description here

Data is loading:
enter image description here

Data is loaded:
enter image description here

3

Answers


  1. Chosen as BEST ANSWER

    I changed it "readyWidget" to "readyBuilder". So, I did it a function returns widget. Now there is no error, it's like I want! ^^

    StateWidgetExampleView:

    class StateWidgetExampleView extends StatefulWidget {
      const StateWidgetExampleView({super.key});
    
      @override
      State<StateWidgetExampleView> createState() => _StateWidgetExampleViewState();
    }
    
    class _StateWidgetExampleViewState extends State<StateWidgetExampleView> {
      bool loading = true;
      String? data;
    
      @override
      void initState() {
        super.initState();
    
        Future.delayed(Duration(milliseconds: 1000)).then((value) {
          data = "Gelen Veri!";
          setState(() {
            loading = false;
          });
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("StateWidgetExampleView"),
          ),
          body: StateWidget(
            loading: loading,
            error: data == null,
            readyBuilder: () => Text(data!),
          ),
        );
      }
    }
    

    StateWidget:

    class StateWidget extends StatelessWidget {
      const StateWidget({
        super.key,
        required this.readyBuilder,
        this.error = false,
        this.errorWidget,
        this.loading = false,
        this.loadingWidget,
      });
    
      final Widget Function() readyBuilder;
      final bool error;
      final Widget? errorWidget;
      final bool loading;
      final Widget? loadingWidget;
    
      @override
      Widget build(BuildContext context) {
        return Builder(
          builder: (context) {
            if (loading) {
              return loadingWidget ?? LoadingCircularProgressIndicatorWidget();
            }
    
            if (error) {
              return errorWidget ?? ErrorIconWidget();
            }
    
            return readyBuilder();
          },
        );
      }
    }
    

  2. The StateWidget widget is being creating from 1st frame. Even though you’ve waited some frame to handle null. But

    This

    body: StateWidget(
      loading: loading,
      error: data == null,
      readyWidget: Text(data!),/// here means create with current data
    ),
    

    So the Text widget doesn’t accept null and while using null-assert, it is throwing the error.

    You’ve hidden the error on UI, not on compiler because Text() widget is creating from 1st frame.

    You can provide default value like readyWidget: Text(data??"").

    Login or Signup to reply.
  3. You can achieve the desired functionality by using a FutureBuilder widget.

    class StateWidgetExampleView extends StatefulWidget {
      const StateWidgetExampleView({super.key});
    
      @override
      State<StateWidgetExampleView> createState() => _StateWidgetExampleViewState();
    }
    
    class _StateWidgetExampleViewState extends State<StateWidgetExampleView> {
      late final Future<String> _data;
    
      @override
      void initState() {
        super.initState();
    
        _data = Future.delayed(const Duration(milliseconds: 1000)).then((value) {
          return "Gelen Veri!";
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text("StateWidgetExampleView"),
          ),
          body: FutureBuilder(
            future: _data,
            builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
              if (snapshot.hasData) {
                return Text(snapshot.data!);
              } else if (snapshot.hasError) {
                return Text('Error: ${snapshot.error}');
              }
    
              return const Center(child: CircularProgressIndicator());
            },
          ),
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search