Simple Beginners Guide to Streams | Flutter and Dart Stream Basics

Поділитися
Вставка
  • Опубліковано 10 лип 2024
  • This video covers the most basic functionality around streams. I have a bonus tip in there in streams and a thank you to the community.
    00:00 - Intro & Setup
    01:03 - Stream Creation
    01:30 - Listen to stream
    02:16 - Add value onto stream
    03:12 - Stream Management
    04:42 - Multiple subscribers & Bad State
    05:10 - Single Subscription Stream
    06:06 - Broadcast Stream
    07:25 - Manual Streams
    📚 Written 📗: www.filledstacks.com/post/a-c...
  • Наука та технологія

КОМЕНТАРІ • 166

  • @tomnijs2639
    @tomnijs2639 4 роки тому

    Excellent work. Clear structure, well-paced, no overhead or useless information.

    • @FilledStacks
      @FilledStacks  4 роки тому

      Thank you. Exactly what I was going for

  • @manesvenom123
    @manesvenom123 4 роки тому

    You are the master of Flutter. Thank you!!!!! I never find a channel like FilledStacks that is incredibly clear and simple for understanding the concept of dart/flutter!!!😎

    • @FilledStacks
      @FilledStacks  4 роки тому

      Thank you Venom 👻 I appreciate your very kind words. I'm happy you found the channel, welcome 😁

  • @leonardoventocilla3275
    @leonardoventocilla3275 4 роки тому +1

    Thanks from Brazil. Great video. Very very clear and direct to the point. Can wait for the next videos !!

    • @FilledStacks
      @FilledStacks  4 роки тому

      Thank you for the feedback ❤️ much love from South Africa 🇿🇦🇿🇦

  • @tylerthornton9611
    @tylerthornton9611 4 роки тому +4

    You know a video is awesome when you wish you could've watched it when you were starting out. Thanks, Dane, for another great vid!

    • @FilledStacks
      @FilledStacks  4 роки тому

      That's a great compliment! Thank you man. I really appreciate it. I'm happy to hear it helped, Streams are a large topic but I think this basis is a good place to start.

  • @robertmorris9185
    @robertmorris9185 2 роки тому +1

    Great content, clear and concise. Really appreciated!

    • @FilledStacks
      @FilledStacks  2 роки тому

      I'm very happy to hear that. Thanks for leaving a comment.

  • @KatlegoMoilwa
    @KatlegoMoilwa 4 роки тому +3

    Your videos are different my good Sir. You have been nothing but an absolute help in my development as Flutter/Dart developer.

    • @FilledStacks
      @FilledStacks  4 роки тому

      Thanks for the great feedback Katlego. I really appreciate it :) I'm happy to help

  • @jessewright870
    @jessewright870 4 роки тому

    Excellent, thanks for making this!

    • @FilledStacks
      @FilledStacks  4 роки тому

      Thank you. you're very welcome 😊

  • @shadowByteX
    @shadowByteX 4 роки тому

    Awesome Dane . This has given me good clarity on streams in flutter

    • @FilledStacks
      @FilledStacks  4 роки тому +1

      That's good to hear! Thank you your watching :)

  • @edungdivinefavour6977
    @edungdivinefavour6977 2 роки тому

    Wow!... Straight to the point!... I learned so much in such a short time. Thanks!

  • @ShikharDadhich
    @ShikharDadhich 2 роки тому

    Wow! What an amazing explanation of stream.

  • @iwandepee
    @iwandepee 3 роки тому +1

    wow, great explanations. thankss

    • @FilledStacks
      @FilledStacks  3 роки тому

      It's my pleasure! Thanks for watching

  • @LalitTaparia574
    @LalitTaparia574 3 роки тому

    Loved it. I have slowed down speed to 0.5 and followed every step and done :-) .

    • @FilledStacks
      @FilledStacks  3 роки тому +1

      haha! Awesome :) I like to keep the videos dense with information so slowing it down is a good idea :) Can you believe some people watch my videos on 1.5x

  • @RaymondAtivie
    @RaymondAtivie 4 роки тому

    Thank you from Nigeria.
    I always look forward to your videos

    • @FilledStacks
      @FilledStacks  4 роки тому

      Thank you ❤️ and thanks for watching.

  • @BrendansReasons
    @BrendansReasons 2 роки тому

    Thank you, this was helpful

    • @FilledStacks
      @FilledStacks  2 роки тому

      Very happy to hear that. Thanks for watching!

  • @1ManStartup
    @1ManStartup Рік тому

    This was really helpful thanks!

    • @FilledStacks
      @FilledStacks  Рік тому

      Very happy to hear that. Thanks for leaving a comment about it.

  • @pavloavdonin6834
    @pavloavdonin6834 3 роки тому

    It's perfect!Thanks!

    • @FilledStacks
      @FilledStacks  3 роки тому

      I'm happy to hear that. And it's my pleasure making the video.

  • @lorraineokl8414
    @lorraineokl8414 4 роки тому

    Thank you for taking the time Dane.

  • @verryondrums
    @verryondrums 4 роки тому

    Awesome tutorial, was great help, thank you!

    • @FilledStacks
      @FilledStacks  4 роки тому

      I'm very happy to hear that, thanks for letting me know.

  • @noobinator9854
    @noobinator9854 4 роки тому

    Awesome video!!!

  • @joshuaadams7353
    @joshuaadams7353 4 роки тому

    This is a great way of explaining stream logic in a very simple way!

    • @FilledStacks
      @FilledStacks  4 роки тому

      Thank you! I'm happy to hear that you think so 😊

  • @saadahmed1662
    @saadahmed1662 4 роки тому

    Thanks, amazing video as always

  • @zeeshux8478
    @zeeshux8478 3 роки тому

    Great man
    ❤️
    big heart for u

    • @FilledStacks
      @FilledStacks  3 роки тому

      Thanks a lot! I appreciate you watching

  • @brunoalfred
    @brunoalfred 3 роки тому

    This helped me a lot most especially when I had a problem manually creating streams and the other kind of stream from the StreamController.

    • @FilledStacks
      @FilledStacks  3 роки тому

      I'm happy to hear that. Thanks for letting me know.

  • @obwe
    @obwe 2 роки тому

    VERY good, subscribed.

  • @oldnoob88
    @oldnoob88 4 роки тому

    very straightforward, thank you!

    • @FilledStacks
      @FilledStacks  4 роки тому

      Exactly what I'm going for. Thank you

  • @espritlibre29
    @espritlibre29 3 роки тому

    thanks for this tutorial, this is really clear and helps me a lot to understand how to work with streams.

    • @FilledStacks
      @FilledStacks  3 роки тому +1

      That's awesome man! Thanks for watching

  • @nithishkumarshanigarapu132
    @nithishkumarshanigarapu132 3 роки тому

    really helpful..Thankyou

    • @FilledStacks
      @FilledStacks  3 роки тому

      I'm happy to help. Thanks for leaving a comment.

  • @goldenpiece7087
    @goldenpiece7087 2 роки тому +1

    I have watched 4 tutorials about streams and yours was the last and best one! Now I understand what stream actually is. Thank you!

    • @FilledStacks
      @FilledStacks  2 роки тому +1

      That's awesome! I'm happy to hear that. Thank you for leaving a comment and letting me know.

  • @harshitsingh6710
    @harshitsingh6710 2 роки тому

    Stream was something I was afraid of, now I love it, thanks to you man

  • @bujupaah
    @bujupaah 4 роки тому +19

    This is an absolute sorcery D:
    What a clear tutorial !
    Thank you ❤️

    • @FilledStacks
      @FilledStacks  4 роки тому

      Haha I am the code magician 🧞‍♂️😊😎🧚🏻‍♂️ Thanks for letting me know.

  • @parsahosseini4241
    @parsahosseini4241 3 роки тому

    Finally, I understood how streams work and how to create them, Thanks a lot buddy👍👍

    • @FilledStacks
      @FilledStacks  3 роки тому

      That's awesome man! I'm happy to have helped.

  • @h3w45
    @h3w45 4 роки тому

    awesome explanation, well done.

    • @FilledStacks
      @FilledStacks  4 роки тому +1

      Thank you. You're very welcome :)

  • @mikel3719
    @mikel3719 4 роки тому

    Nicely done

    • @FilledStacks
      @FilledStacks  4 роки тому

      Thank you! I hope I covered everything you needed :)

  • @manmohanrathi6283
    @manmohanrathi6283 3 роки тому +3

    For some reason streams are quite confusing for a beginner but thank you so much, your explanation is priceless.✨✨✨❤

    • @FilledStacks
      @FilledStacks  3 роки тому +1

      It is, this is the reason I wanted to make this video. It's not that difficult if you break down the smaller concepts behind it.

  • @satendraraj9361
    @satendraraj9361 3 роки тому

    Been struggling for days in the articles until found this !

    • @FilledStacks
      @FilledStacks  3 роки тому

      Wooohooo! That's great. Thanks for letting me know. I'm happy you found it useful

  • @bjugdbjk
    @bjugdbjk 4 роки тому

    Great work

  • @kishansinhparmar
    @kishansinhparmar 4 роки тому +1

    always clean

  • @drimadoh
    @drimadoh 4 роки тому

    Thanks @FilledStacks for this.. I really needed to understand streams properly... You rock!
    Can you give real cases of when would using streams is advisable for real life projects please?... Other than receiving results from Firestore.

    • @FilledStacks
      @FilledStacks  4 роки тому +1

      When you have functionality that requires continuous updates to be handled the same way. Messages coming through, input events from the user, key inputs, anything really. Streams are another tool that you can use to reduce boiler plate code. If you want to sync data across your app you can have a stream that 10 UI elements suibscribe to and as new data comes in they can all update at the same time. There's lots of use cases for it. But like everything else it's just another tool to use when required.

  • @samroodali5141
    @samroodali5141 4 роки тому

    Thank You.

  • @ayhanexe9455
    @ayhanexe9455 3 роки тому

    Thank you brother this is so helpful tutorial can you make stream builder tutorial ?

    • @FilledStacks
      @FilledStacks  3 роки тому

      You're very welcome. I don't think I will because I don't use that in production. I only make videos about code I use in production.

  • @ruipereira2180
    @ruipereira2180 3 роки тому

    Best explanation! Google should hire you to do tutorials!

    • @FilledStacks
      @FilledStacks  3 роки тому

      haha, well I am a GDE so they hired me without paying me lol

  • @RoboEvercool
    @RoboEvercool 4 роки тому

    Great tutorial! I would love to see a tutorial of sending video in packets using streams

    • @FilledStacks
      @FilledStacks  4 роки тому +2

      Thanks Everman, that would be a cool tutorial. I guess in the concept it would be the same as reading chunks of a file from disk and emitting the values over a stream. Not likely that I would do that in a video, since it's an implementation details of a specific use case. There's more broader topics I'd like to cover first. It would be a normal file read in a loop while file.hasData() and you'll yield a List that you'll send up or you can use file sending functionality from the http package and you don't have to do that 😅

  • @kieronwiehahn2828
    @kieronwiehahn2828 4 роки тому

    My first subscription to a fellow South African. What city you from? And big ups for answering every comment.

    • @FilledStacks
      @FilledStacks  4 роки тому

      Firstly, Thanks very much man. Secondly, how crazy is it that I'm your first SA sub. There should be more South African youtubers out there (outside of vlogs). I live in Paarl, Western Cape. Moved back here a few months ago from East London, before that Durban ✈️

    • @FilledStacks
      @FilledStacks  4 роки тому

      Also. Gotta reply man, if people are willing to give me their time the least I can do is answer questions or concerns they have.

  • @murtazavohra360
    @murtazavohra360 3 роки тому

    thanks

  • @wmorrison1967
    @wmorrison1967 4 роки тому

    This is good Dane. Could you provide an example of a real world implementation of streams

    • @FilledStacks
      @FilledStacks  4 роки тому

      Thanks. I've done that in this video :) ua-cam.com/video/qa6A2TOqY0A/v-deo.html

  • @chaitanyabharatdokara4978
    @chaitanyabharatdokara4978 2 роки тому

    What is vscode extension used in the video to get widgets in the tree model?

    • @FilledStacks
      @FilledStacks  2 роки тому

      It's called dart code. Search in preferences for flutter ui and enable both check boxes that come up.

  • @yashshukla3535
    @yashshukla3535 3 роки тому

    Thank you sir for this knowledgeable video,
    But i have a query , what is the benefit of this stream which uses " While " clause. please enlighten me is it takes less data(internet) then Future which uses "while" for this kinds of stuff .

    • @FilledStacks
      @FilledStacks  3 роки тому

      You're welcome. There's no benefit to it. I was showing how you can create a manual stream out of a function without using a controller. If you don't need it you shouldn't use it.

  • @dhinesh534
    @dhinesh534 3 роки тому

    I am listening to a stream which emits the Sms whenever a new sms is received, could you please tell me how the same can be achieved even when the app is running in the background? Thanks in advance

    • @FilledStacks
      @FilledStacks  3 роки тому

      The same can't be achieved through those same patterns. The sms will be handled by a background handler. You'll have I think 30 seconds to do any background work without the app launching into the foreground. Read about background services in Android and ios

  • @bennguyen1313
    @bennguyen1313 4 роки тому

    When do you know to use 'stateless' widgets (MyApp), vs state widget (_MyAppState)? BTW, a second after the 1m22s mark, the code appears "_MyAppState createState() => _MyAppState()" was this auto-generated when you replaced the "stateless" widget, or did you enter that manually?
    Regarding canceling the subscription when your app disposes.. What does the '?' do in 'streamSubscription?.cancel()' (4m18s mark). I would have assumed it's a typo, but Dart didn't seem to complain!
    Whether calling the default single-listener Controller constructor.. or using the .broadcast version, can a class from another file get a handle to the controller.stream and listen for events.. or only functions within _MyAppState. Similarly, can any function, either within the class or outside of it, push things into the pipe (controller.add(12))?
    Does the 'async' keyword in onPressed means that the GUI will still be responsive while 'await'ing the data? i.e. what happens if you just call the Future getDelayedRandomValue() async; Similarly must Stream/async*/yield always go together?

    • @FilledStacks
      @FilledStacks  4 роки тому

      Yes, that code was auto generated. Please keep in mind that my videos is not for beginners in programming. It's for beginners in Flutter that already know how to program. The null operator ? is to check something is null before calling to ensure you don't try to call something on an object that's null.
      Scope works the same in dart as any other object oriented programming language so outside of scope variables are outside of scope.
      Async is required if you want to use await within a function. If you just call the function it will fire and forget

  • @AbhishekMishra-fr7po
    @AbhishekMishra-fr7po 3 роки тому

    Sir awesome video..i wanted to know how can we cancel the subscription from our getdelayedRandomValue() stream

    • @FilledStacks
      @FilledStacks  3 роки тому

      Thank you. You store the stream subscribption and then call cancel on that when you're ready to cancel.

  • @julianarnold7992
    @julianarnold7992 4 роки тому

    I'm new to this stuff. Can you choose between future asyncs and streams just like you can choose between iteration and recursion?

    • @FilledStacks
      @FilledStacks  4 роки тому +1

      Kind of. The example at the end should cover that question. Futures are for performing an asynchronous task and getting the results when done. Streams are when you have a task that won't finish in one action but instead will emit results over time that you have to handle.

  • @Kooshad1
    @Kooshad1 4 роки тому

    for your emulator, it's this avd with android studio or what is it from?

    • @FilledStacks
      @FilledStacks  4 роки тому

      It's just a basic AVD, I cloned the Pixel2 as my image and I launch it through Visual Studio code. I don't use Android Studio for development but you have to use it to open the AVD manager (I wish they could change that).

  • @mruduladdipalli5417
    @mruduladdipalli5417 3 роки тому

    10 minutes went like a flash, easily explained

    • @FilledStacks
      @FilledStacks  3 роки тому

      Awesome!! And thanks for watching and leaving a comment. I appreciate it

  • @jasonwei6722
    @jasonwei6722 4 роки тому

    Nice one! Stream basic, then BLOC?

    • @FilledStacks
      @FilledStacks  4 роки тому +1

      Haha, there are so many BLoC videos out there. I also don't like BLoC, which is why I'm not making any videos about it. I'd recommend ResoCoder's video and Robert Brunhage, they have a series on BLoC development that's very well done.

  • @smttartu
    @smttartu 4 роки тому

    How can we use this in different widgets, not in the same widget ? I tried it, it doesnt listen :/ . What I want to do is that I want to send some values from widget, and listen that in another widget and take some actions according to sent values. Thank you.

    • @FilledStacks
      @FilledStacks  4 роки тому

      Add it into a class that's accessible by both widget. (www.filledstacks.com/snippet/use-rx-dart-in-flutter-to-synchronize-ui/)www.filledstacks.com/snippet/use-rx-dart-in-flutter-to-synchronize-ui/ ) that uses rxdart but you can replace it with streams, it's the same interface.

  • @paulovictorespindola7496
    @paulovictorespindola7496 2 роки тому

    i think i can use stream to sync my firebase with my local sql.
    am i rigth? D:

  • @eatingprimal5233
    @eatingprimal5233 4 роки тому

    I have an error caused by this line -> StreamController controller = StreamController();
    Error -> Close instances of `dart.core.Sink`.

    • @FilledStacks
      @FilledStacks  4 роки тому +1

      That should be a warning and not an error? To get it away add a dispose function and close your controller in there.

  • @MapMavericks
    @MapMavericks Рік тому

    Thank you

    • @FilledStacks
      @FilledStacks  Рік тому

      You're very welcome. Thank you for watching.

  • @shadowByteX
    @shadowByteX 4 роки тому

    Please make a video on integrating rest api (not firebase 😅) with complex JSON . Which have CRUD operations in it .

    • @FilledStacks
      @FilledStacks  4 роки тому +1

      In my architecture video I show how I setup my API. The video is not about that or how to do it, but you can see the code. It's 4 lines per function so it's easy to understand. ua-cam.com/video/kDEflMYTFlk/v-deo.html Look at the api class in the source code for that tutorial. I don't do much more than that, sometimes I'll keep a login token locally if needed, but that should cover the basics of an api integration.

  • @jacowillemse72
    @jacowillemse72 4 роки тому

    Hi Dane, Thanks for your tutorials, I just have one question I would really appreciate if you could spare a minute to help me here, I followed your tutorial - "Easy Flutter Location Service for Realtime Updates in Provider" but need to listen/subscribe to the stream that is continuously emitting location updates using the method you illustrate in this tutorial, I am importing the "location_service.dart" file and trying to listen to it on button press using Stream stream = _locationController.stream;
    stream.listen((value){print('coordinates')}); The reason I can't follow the rest of your tutorial showing how to do it using provider is that in my main.dart file I am already wrapping my app in another StreamProvider to handle authentication. Dankie:-)/Thank you!

    • @jacowillemse72
      @jacowillemse72 4 роки тому

      My current main.dart: would it be easier adding both streams at this level and if so how can one have two streams at the same time? class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
      return StreamProvider.value(
      value: AuthService().user,
      child: MaterialApp(
      //home: Wrapper(),
      routes: {
      '/': (context) => Wrapper(),
      '/home': (context) => Home(),
      '/splash': (context) => Splash(),
      '/events': (context) => Events(),
      '/profile': (context) => Profile(),
      },
      ),
      );
      }
      }

    • @FilledStacks
      @FilledStacks  4 роки тому +1

      Hello :) You can place the stream providers as one of the providers in a MultiProvider. So instead of wrapping your app with a StreamProvider wrap it with a MultiProvider and provide as many things as you want in the childrens list ;)

    • @jacowillemse72
      @jacowillemse72 4 роки тому

      @@FilledStacks many thanks, Dane!

  • @yonasmelesse8558
    @yonasmelesse8558 4 роки тому

    can you please make a tutorial on .map() related with Stream

    • @FilledStacks
      @FilledStacks  4 роки тому

      A full tutorial on that will definitely not happen because it'll be a 1 minute video lol.

  • @ShitIndie
    @ShitIndie 4 роки тому

    Good good good, double good.

  • @himsha64
    @himsha64 4 роки тому +1

    Do some Tutorials on RxDart, btw Thanks.

    • @FilledStacks
      @FilledStacks  4 роки тому +1

      I don't use it in production so I probably won't be making any in the near Future. And you're welcome :)

  • @nguye1971
    @nguye1971 Рік тому

    So..how to add to the stream from another class?

    • @FilledStacks
      @FilledStacks  Рік тому

      Same way you would get any other value from another class. YourClass().value

  • @robertoferro8512
    @robertoferro8512 4 роки тому

    I hear a South African accent and I up vote.

  • @sankaranarayananp8407
    @sankaranarayananp8407 2 роки тому

    Is stream will affect the performance of our app ?

    • @FilledStacks
      @FilledStacks  2 роки тому

      No

    • @sankaranarayananp8407
      @sankaranarayananp8407 2 роки тому

      @@FilledStacks sir, i have lot of other doubts about stream. So how can i contact you ?

    • @FilledStacks
      @FilledStacks  2 роки тому +1

      @@sankaranarayananp8407 You can join the Slack here join.slack.com/t/filledstacks/shared_invite/zt-mcw04u5t-dTeyH0lPONuzd9i0osk9Gw and ask questions. There's 2000 flutter devs there that can help you.

  • @glyphicon330
    @glyphicon330 4 роки тому

    Whats the plugin that shows the code like a hierarchical tree?

    • @FilledStacks
      @FilledStacks  4 роки тому

      Dart code in VS code. You have to enable it. Go to preferences or settings and type flutter UI then enable the two options.

    • @glyphicon330
      @glyphicon330 4 роки тому

      @@FilledStacks I was searching for an "extension" like crazy everywhere for like hour LoL.
      Thanks man!!!

    • @FilledStacks
      @FilledStacks  4 роки тому +1

      @@glyphicon330 haha. Yeah, they released it in v3.1 and had a blog post about it somewhere. It wasn't very public lol

    • @glyphicon330
      @glyphicon330 4 роки тому

      @@FilledStacks Nice.
      Man you got a great channel I just subscribed and I'll be sharing your videos. Keep it up 👍🏼

  • @williamsalazar2624
    @williamsalazar2624 4 роки тому

    There is a video with Bloc

  • @alex_kenbo
    @alex_kenbo 4 роки тому

    Everything is clear.
    But UA-cam for some reason defines your language as Dutch, and this makes auto-translation comic =)
    Maybe you will choose the video - the English language is somewhere in the settings.

    • @FilledStacks
      @FilledStacks  4 роки тому +1

      Haha, probably because of my Afrikaans accent. It's all set to English 😊 I think it might be an auto CC thing youtube is doing.

    • @alex_kenbo
      @alex_kenbo 4 роки тому

      @@FilledStacks Maybe =)
      I thought you were a Dane.
      Nevertheless, I went through a tutorial writing down the code for you locally. And the flows have become a little clearer.
      Thank!

  • @warperone
    @warperone 3 роки тому

    Great videos - if possible if you can go a little slower and take more time to explain what is going on with the code - more detail and illustrate concepts. Only a suggestion.

    • @FilledStacks
      @FilledStacks  3 роки тому +1

      Thanks for the suggestion. Unfortunately I don't want to spend more time on the videos. I think the amount of detail I put into the tutorial along with the written tutorial for every video should be more than enough. It takes 2 days to write the tutorial, create the code, make sure it works, record the code, do voice over, edit the video, upload everywhere etc. The written tutorial has way more details so I'd suggest reading that as well :)

  • @gyenabubakar
    @gyenabubakar 2 роки тому +1

    I was expecting a more beginner-friendly approach

    • @FilledStacks
      @FilledStacks  2 роки тому +1

      This is a beginner friendly approach. Not to beginners in programming, but beginners in flutter.

  • @tepoktangan5076
    @tepoktangan5076 2 роки тому

    how to cancel/close/unsubcribe manual stream?

    • @FilledStacks
      @FilledStacks  2 роки тому

      Save the stream subscription and call dispose on it when you want to cancel it.

    • @tepoktangan5076
      @tepoktangan5076 2 роки тому

      @@FilledStacks in 9:18, how i stop the getting value from manual stream

    • @FilledStacks
      @FilledStacks  2 роки тому

      @@tepoktangan5076 the same as a normal stream. Store the subscription then dispose it when you don't want to listen anymore

    • @tepoktangan5076
      @tepoktangan5076 2 роки тому

      @@FilledStacks oh i see

    • @tepoktangan5076
      @tepoktangan5076 2 роки тому

      @@FilledStacks thanks

  • @marwan.v1511
    @marwan.v1511 4 роки тому

    you are talking fast bro 😅 , thanks that was a good explanation .

    • @FilledStacks
      @FilledStacks  4 роки тому +1

      haha Can't be wasting those valuable seconds :D

  • @balajijangde8470
    @balajijangde8470 2 роки тому

    reach++

  • @samehkhemira1309
    @samehkhemira1309 4 роки тому

    thank you this is very helpful