0

I am working on a musicplayer application. I have to retrieve data like thumbnail,name,authors,etc for every song. So I have used the following code just above the return of build method

...@override
Widget build(BuildContext context) {
//For song index and details
final themeNotifier = Provider.of<ThemeNotifier>(context, listen: true);
audioFunctions = themeNotifier.getAudioFunctions();
themeNotifier.getSongIndex().then((value) {
  setState(() {
    index = value;
  });
});
thumbnailPath =
    audioFunctions.songs[index].albumArtwork ?? 'assets/thumbnail.jpg';
title = audioFunctions.optimiseSongTitles(index) ?? title;
author = audioFunctions.songs[index].artist ?? author;

//For the bg-gradient and device height
double deviceHeight = MediaQuery.of(context).size.height;
final light = Theme.of(context).primaryColorLight;
final dark = Theme.of(context).primaryColorDark;

return Scaffold(...

bst365体育投注网址大全Is this a bad practise? If so, how can I do it the right way?

2
1

Yes it is. According to :

Although it’s convenient, it’s not recommended to put an API call in a build() method.

bst365体育投注网址大全Flutter calls the build() method every time it needs to change anything in the view, and this happens surprisingly often. Leaving the fetch call in your build() method floods the API with unnecessary calls and slows down your app.

The solution: Either use a FutureBuilder widget like so:


class _MyAppState extends State<MyApp> {
  Future<int> songIndex;
  
  @override
  void didChangeDependencies(){
  songIndex = Provider.of<ThemeNotifier>(context, listen: false).getSongIndex();
  super.didChangeDependencies();
  }
  
  @override
  Widget build(BuildContext context) {

    //For the bg-gradient and device height
    double deviceHeight = MediaQuery.of(context).size.height;
    final light = Theme.of(context).primaryColorLight;
    final dark = Theme.of(context).primaryColorDark;
    return FutureBuilder<int>(
      future: songIndex,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          final index = snapshot.data;
          thumbnailPath = audioFunctions.songs[index].albumArtwork ??
              'assets/thumbnail.jpg';
          title = audioFunctions.optimiseSongTitles(index) ?? title;
          author = audioFunctions.songs[index].artist ?? author;
          return Scaffold(body: Container());
        } else if (snapshot.hasError) {
          return Text("${snapshot.error}");
        }

        return CircularProgressIndicator();
      },
    );
  }
}

bst365体育投注网址大全Notice that:

final themeNotifier = Provider.of<ThemeNotifier>(context, listen: false);

listen is false because I don't think we need to rebuild the widget each time the provider changes its state (because we only use it for executing the method).

Or [the better approach] to put the logic inside the ThemeNotifierbst365体育投注网址大全 class. Like so:

bst365体育投注网址大全In the theme notifier class:


class ThemeNotifier with ChangeNotifier {
  //whatever state and logic you have

  //state for you current song index
  int _index;

  int get index => _index;

  Future<void> getSongIndex() {
    // some logic to retreive the new index
    _index = newIndex;
    notifyListeners();
  }
}

In the place you provided the ThemeNotifier

Widget build() => ChangeNotifierProvider(
      create: (_) => ThemeNotifier()..getSongIndex(),
      child: //whatever,
    );

In the place you used it (consumed it):

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //For the bg-gradient and device height
    double deviceHeight = MediaQuery.of(context).size.height;
    final light = Theme.of(context).primaryColorLight;
    final dark = Theme.of(context).primaryColorDark;
    return Consumer<ThemeNotifier>(
      builder: (context, themeNotifier, _) {
        final index = themeNotifier.index;
        if (index == null) return CircularProgressIndicator();

        thumbnailPath =
            audioFunctions.songs[index].albumArtwork ?? 'assets/thumbnail.jpg';
        title = audioFunctions.optimiseSongTitles(index) ?? title;
        author = audioFunctions.songs[index].artist ?? author;
        return Scaffold(
          body: // continue here,
        );
      },
    );
  }
}

This was is using the Consumer syntax which is better if you don't want to rebuild the whole MyApp widget in this case each time the provider calls notifyListeners(). But a simpler way if the rebuilding doesn't matter (like in this case) is this:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //For the bg-gradient and device height
    double deviceHeight = MediaQuery.of(context).size.height;
    final light = Theme.of(context).primaryColorLight;
    final dark = Theme.of(context).primaryColorDark;
    final themeNotifier = Provider.of<ThemeNotifier>(context);
    final index = themeNotifier.index;
    if (index == null) return CircularProgressIndicator();

    thumbnailPath =
        audioFunctions.songs[index].albumArtwork ?? 'assets/thumbnail.jpg';
    title = audioFunctions.optimiseSongTitles(index) ?? title;
    author = audioFunctions.songs[index].artist ?? author;
    return Scaffold(body: null // continue here,
        );
  }
}

Final thoughts: I advise you to extract this logic:

   thumbnailPath = audioFunctions.songs[index].albumArtwork ??
              'assets/thumbnail.jpg';
   title = audioFunctions.optimiseSongTitles(index) ?? title;
   author = audioFunctions.songs[index].artist ?? author;

Into the provider itself.

| improve this answer | |
New contributor
Ahmed Erabti is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
  • 1
    Thanks for your quick response. I have made it work with FutureBuilder. Did'nt quite understood the [the better approach]. Can you direct me to some relavant articles. I'am going to add the details to the provider too. Thanks – Hosea varghese Jul 1 at 6:14
  • Sure thing: – Ahmed Erabti Jul 1 at 6:20
  • 1
    The example usage of FutureBuilder here provides no benefit over the OP's usage of .then. The Future is still being created in the build method and will lead to the async task being restarted with every rebuild of the parent - the same behavior as the OP's code. See : "The future must have been obtained earlier, e.g. during State.initState, etc. It must not be created during the State.build or StatelessWidget.build method call when constructing the FutureBuilder." – Christopher Moore Jul 1 at 20:25
  • @ChristopherMoore you are right I missed that, I corrected myself, thanks. – Ahmed Erabti Jul 1 at 21:23
  • @ChristopherMoore isn't using didChangeDependencies safer that initState? – Ahmed Erabti Jul 2 at 5:10
0
1

For any kind of widget, you will most likely need some preparation (e.g. setting a variable's initial value or adding a listener to a FocusNode).

That being said, for StatelessWidget I've not come across any way to do it then doing so in the beginning of the build function.

For StatefulWidget, you can do all this by overriding the initState method. This where you typically set up listeners or set the initial value of a TextEditingController.

For any widget that requires awaiting some Future before rendering, I would recommend FutureBuilder since this easily allows you to handle all the different snapshot conditions and / or ConnectionState.


In your case, I don't see the problem with things like

//For the bg-gradient and device height
double deviceHeight = MediaQuery.of(context).size.height;
final light = Theme.of(context).primaryColorLight;
final dark = Theme.of(context).primaryColorDark;

since you can think of these as just making a sort of abbreviation for the otherwise long expressions. For example, repeatedly writing light would be much easier compared to Theme.of(context).primaryColorLight.

bst365体育投注网址大全However, for things like this

themeNotifier.getSongIndex().then((value) {
  setState(() {
    index = value;
  });
});

depending on what exactly getSongIndex() does, what types of errors or delays it can cause, you might want to consider other options such as FutureBuilder.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policybst365体育投注网址大全

Not the answer you're looking for? Browse other questions tagged or ask your own question.