0

Considering we have a function like following that loads up data from database:

  loadData() async {
    loadedData = await loadingData();
    return loadedData;
  }

And we want to use this loadedData inside the same or other class/widget using Provider package.

I tried many ways like putting loadData() inside initState() or didChangeDependencies() like following:

  @override
  void didChangeDependencies() async {
    super.didChangeDependencies();
    data = await Provider.of<>(context , listen: false).loadData();
  }

Or using Consumer or trying to pass data using ChangeNotifierProxyProvider at the startup of the application but still have problems most of the time and getting errors like null value errors or some other related errors(Can't remember at the moment).

I like to know what is the correct way of fetching data from database and wait for data in a Widget?

For example I know if I change (listen: false) to (listen: true) within the Provider command I can read data but it seems it updates the app very much and I can hear the rising noise of the CPU fan that I don't like it!

Please give me a simple code example/snippet to understand the correct way of doing that?

best_of_man
  • 643
  • 2
  • 15

3 Answers3

1

You can use FutureBuilder to load data like this:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: Provider.of<MyData>(context, listen: false).loadData(),
      builder: (BuildContext context, AsyncSnapshot<MyData> snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return Center(
            child: CircularProgressIndicator(),
          );
        } else if (snapshot.hasError) {
          return Center(
            child: Text('Error: ${snapshot.error}'),
          );
        } else {
          MyData data = snapshot.data!;
          // Use the loaded data to build your widget
          return Text(data.someProperty);
        }
      },
    );
  }
}
Hamed
  • 5,867
  • 4
  • 32
  • 56
1

Here is an example of loadData using the provider. If you don't want to eager load the data, just remove initState() method.

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
    void initState() {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        context.read<TestModel>().loadData();
      });
      super.initState();
    }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: _buildContent()
    );
  }

  Widget _buildContent() {
    ///context.watch only rebuild when [TestModel] changes.
    TestModel testModel = context.watch<TestModel>();
    if (testModel.isLoading) {
        return Center(child: CircularProgressIndicator());
      } else {
        return Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(testModel.data),
              ElevatedButton(
                onPressed: () async => await testModel.loadData(),
                child: Text('Click to Load Data'),
              ),
            ],
          ),
        );
      }
    }
  }

class TestModel with ChangeNotifier {
  String _data = '';
  bool _isLoading = false;

  String get data => _data;
  bool get isLoading => _isLoading;

  Future<void> loadData() async {
    _isLoading = true;
    notifyListeners();
    // your logic here
    await Future.delayed(Duration(seconds: 2));
    _data = 'Finish loading data with result';
    _isLoading = false;
    notifyListeners();
  }
}
Sowat Kheang
  • 668
  • 2
  • 4
  • 12
0

You can use FutureBuilder or via using turnery operators.

data == null || data.isEmpty
? CircularProgressIndicator() 
: Center(
    child: Text("It is working")
),