Thinking & coding: Turning 180° with the code, a good lesson learned.

In this session I wanted to bring the eventcard into the listview. And then also the data of the backend. After all this I want to clean up the code a bit.

I will still need a provider

Still, I want to somehow separate the FirestoreService from the UI. I wanted to do this by extracting the widgets in the FirestoreService. But then I will still need a provider, a stream provider. Maybe I will switch to a FutureProvider later. I am not yet sure of that. I want to use Riverpod because I need 2 providers of the same type.

I learned a good lesson

So this session I learned a good lesson. If I want to separate the UI and the logic or backend I always need stat management. So I have to restore my thinking to the original idea. This will certainly not be the last time I have to overthrow my code to achieve my goal. But don't worry, we're flying back in there.

A resume of what I have and should do.

  • I have a Firestore service that reads the events from the backend.
  • In the Firestore service I have placed an Event Model in such a way that this data is mapped in a list to the shape of my custom EventModel.
  • Now I'm going to build a provider that will make this modeled list of data available to the UI.

I choose a StreamProvider

After a thorough study of the Riverpod documentation, I choose a StreamProvider to make the data accessible to the UI. And in combination I am going to use Hooks. Why Hooks? Because this requires the least boillerplate code, and makes everything much more readable. Still for myself. So in Pubspec.yaml I added the dependencies of Flutter Riverpod and Hooks.

Writing the code.

First I need to add the Provider Scope to the Main Widget. Such that all providers are available for the entire Widget tree. Code line 15 below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:my_first_production/widgets/my_app.dart';





void main() async {
  // Binding and initialising of Firebase in Flutter app
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}

Declare the StreamProvider

Then I have to declare the StreamProvider globally, and I do that in a separate file EventProvider.dart in the folder Providers. Because later on there will be a FoodProvider to build a list of restaurants and drinking places in another part of my app.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import 'package:hooks_riverpod/all.dart';
import 'package:my_first_production/models/event_model.dart';
import 'package:my_first_production/services/firestore_event_service_next.dart';

final eventProvider = StreamProvider.autoDispose<List<Events>>(
  (ref) {
    final firestoreService = FirestoreEventServiceNext();
    Stream<List<Events>> listOfEvents = firestoreService.getEvents();
      return listOfEvents;
  },
);


Autodispose

At first I had some trouble adding ".autodispose" to my StreamProvider. But after the complete restart of the VSC, there was suddenly no problem anymore.

Test my state management

In the EventScreen I then have to test my state management. Implemented a simple Listview.builder. Why a listview.builder? Well according to the documentation, if you want to make a list with an unprecedented number of data elements, you have to use the builder. So read so done. At least I've already seen code on the net that uses a regular Listview.


In Code Line 14 I created my StreamProvider object.

In Code line 37 I wrote A Streambuilder who will prepare the data list of my Stream provider for use. With code line 41-46 catching a possible read error from the backend and a catch for while the stream is loading.

In Code Line 50 I then wrote a Listview.builder. Which will read and display my data from the backhand repeatedly, and this for the time being in a ListTile.


And as you can see, I can now use the data through my own EventModel variables.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/all.dart';
import 'package:my_first_production/models/event_model.dart';
import 'package:my_first_production/owner_data/owner_data.dart';
import 'package:my_first_production/providers/event_provider.dart';

class EventScreen extends HookWidget {
  const EventScreen({Key key}) : super(key: key);

  
  @override
  Widget build(BuildContext context) {
    Stream<List<Events>> lijstjes = useProvider(eventProvider.stream);
    return Container(
      decoration: BoxDecoration(
        //color: Colors.white,
        image: DecorationImage(
            image: AssetImage(eventScreenBackground), fit: BoxFit.fitWidth),
      ),
      child: Expanded(
        child: Scaffold(
          backgroundColor: Colors.transparent,
          body: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              SizedBox(height: 53),
              Container(
                padding: const EdgeInsets.only(
                    left: leftPaddingNavigationScreenWidgets),
                child: Text(
                  eventScreenTitle,
                  style: Theme.of(context).textTheme.headline1,
                ),
              ),
              StreamBuilder<List<Events>>(
                  stream: lijstjes,
                  builder: (BuildContext context,
                      AsyncSnapshot<List<Events>> snapshot) {
                    if (snapshot.hasError) {
                      return Text('Something went wrong');
                    }

                    if (snapshot.connectionState == ConnectionState.waiting) {
                      return Text("Loading");
                    }

                    return Expanded(
                      child: ListView.builder(
                        itemCount: snapshot.data.length,
                        itemBuilder: (context, index) {
                          return ListTile(
                            title: Text(snapshot.data[index].eventTitle),
                            subtitle: Text(snapshot.data[index].eventDate),
                          );
                        },
                      ),
                    );
                  })
            ],
          ),
        ),
      ),
    );
  }
}

After a long toil and sweat, with a less friendly call here and there to every possible Saint, I managed to get the code working. Already in this preliminary test.


The next step will be to implement my already written own EventCard in the code. And to get a representation like in the prototype.


I hope you can follow my thinking a little bit, and that you understand why I took which steps. So I can do it too. So don't give up. It took me 12 hours to get all of this up and running without any errors.


If there are any errors in my code or mindset, please let me know in the comment section below this article.


Until the next


Do, believe and be happy,

Stefaan


A big thanks to Andy Julow, the architect of the snippets of code I used. I also followed his thinking process. Thank You Sir.

Also Thanks to Max Weber for the moral support.

Comments

Popular posts from this blog

Thinking & coding: Linking Firebase to my Flutter App. Watch out for a important detail.

Software developer yes, but where to begin?

Thinking & coding: Small but important code implementations.