How to wait for a request to complete using ObservableFuture?

Viewed 51

When I transition to a screen where I get a list of information via an API, it initially gives an error:

_CastError (Null check operator used on a null value)

and only after loading the information, the screen is displayed correctly.

I am declaring the variables like this:

@observable
ObservableFuture<Model?>? myKeys;

@action
getKeys() {
  myKeys = repository.getKeys().asObservable();
}

How can I enter the page only after loading the information?

In button action I tried this but to no avail!

await Future.wait([controller.controller.getKeys()]);
Modular.to.pushNamed('/home');

This is the page where the error occurs momentarily, but a short time later, that is, when the api call occurs, the data appears on the screen.

class MyKeyPage extends StatefulWidget {
  const MyKeyPage({Key? key}) : super(key: key);

  @override
  State<MyKeyPage> createState() => _MyKeyPageState();
}

class _MyKeyPageState
    extends ModularState<MyKeyPage, KeyController> {
  KeyController controller = Modular.get<KeyController>();

  Widget countKeys() {
    return FutureBuilder(
      builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
        final count =
            controller.myKeys?.value?.data!.length.toString();
        if (snapshot.connectionState == ConnectionState.none &&
            !snapshot.hasData) {
          return Text('..');
        }
        return ListView.builder(
            scrollDirection: Axis.vertical,
            shrinkWrap: true,
            itemCount: 1,
            itemBuilder: (context, index) {
              return Text(count.toString() + '/5');
            });
      },
      future: controller.getCountKeys(),
    );
  }

  @override
  Widget build(BuildContext context) {
    Size _size = MediaQuery.of(context).size;

    return controller.getCountKeys() != "0"
        ? TesteScaffold(
            removeHorizontalPadding: true,
            onBackPressed: () => Modular.to.navigate('/exit'),
            leadingIcon: ConstantsIcons.trn_arrow_left,
            title: '',
            child: Container(
              width: double.infinity,
              child: Padding(
                padding: const EdgeInsets.only(left: 24),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'Keys',
                      style: kHeaderH3Bold.copyWith(
                        color: kBluePrimaryTrinus,
                      ),
                    ),
                    countKeys(),
   
                  ],
                ),
              ),
            ),
            body: Observer(builder: (_) {
              return Padding(
                padding: const EdgeInsets.only(bottom: 81),
                child: Container(
                  child: ListView.builder(
                    padding: EdgeInsets.only(
                      left: 12.0,
                      top: 2.0,
                      right: 12.0,
                    ),
                    itemCount: 
                    controller.myKeys?.value?.data!.length,
                    itemBuilder: (context, index) {
                      var typeKey = controller
                          .myKeys?.value?.data?[index].type
                          .toString();

                      var id =
                          controller.myKeys?.value?.data?[index].id;

                      
                      final value = controller
                          .myKeys?.value?.data?[index].value
                          .toString();

                      return GestureDetector(
                        onTap: () {
                          .
                          .
                        },
                        child: CardMeyKeys(
                          typeKey: typeKey,
                          value: value!.length > 25
                              ? value.substring(0, 25) + '...'
                              : value,
                          myKeys: pixController
                              .minhasChaves?.value?.data?[index].type
                              .toString(),
                        ),
                      );
                    },
                  ),
                ),
              );
            }),
            bottomSheet: ....
          )
        : TesteScaffold(
            removeHorizontalPadding: true,
            onBackPressed: () => Modular.to.navigate('/exit'),
            leadingIcon: ConstantsIcons.trn_arrow_left,
            title: '',
            child: Container(
              width: double.infinity,
              child: Padding(
                padding: const EdgeInsets.only(left: 24),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      '...',
                      style: kHeaderH3Bold.copyWith(
                        color: kBluePrimaryTrinus,
                      ),
                    ),
                  ],
                ),
              ),
            ),
            body: Padding(
              padding: const EdgeInsets.only(bottom: 81),
              child: Container(
                child: Center(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      Image.asset(
                        'assets/images/Box.png',
                        fit: BoxFit.cover,
                        width: 82.75,
                        height: 80.91,
                      ),
                      SizedBox(
                        height: 10,
                      ),
                      
                      
                    ],
                  ),
                ), //Center
              ),
            ),
            bottomSheet: ...
          );
  }

  
  List<ReactionDisposer> disposers = [];

  @override
  void initState() {
    super.initState();
    controller.getKeys();
  }

  @override
  void dispose() {
    disposers.forEach((toDispose) => toDispose());
    super.dispose();
  }
}

Initially the error occurs in this block

value: value!.length > 25
? value.substring(0, 25) + '...'
: value,

_CastError (Null check operator used on a null value)

I appreciate if anyone can help me handle ObservableFuture correctly!

2 Answers

You need to call the "future" adding

Future.wait

(the return type of getKeys) keys=await Future.wait([
controller.getKeys();
]);

The problem is your getKeys function isn't returning anything, so there's nothing for your code to await. You need to return a future in order to await it.

Future<Model?> getKeys() {
  myKeys = repository.getKeys().asObservable();
  return myKeys!; // Presumably this isn't null anymore by this point.
}

...

await controller.controller.getKeys();
Modular.to.pushNamed('/home');
Related