{"id":334,"date":"2018-07-09T15:54:34","date_gmt":"2018-07-09T13:54:34","guid":{"rendered":"https:\/\/billigeplaetze.com\/?p=334"},"modified":"2024-08-14T13:36:31","modified_gmt":"2024-08-14T11:36:31","slug":"my-date-with-dart-and-flutter","status":"publish","type":"post","link":"https:\/\/craftcoders.app\/my-date-with-dart-and-flutter\/","title":{"rendered":"My date with Dart and Flutter"},"content":{"rendered":"
…Hey, guys! It’s time for another blog post from me. This time I want to write about my experiences with Flutter and Dart. Every software developer is regularly confronted with new concepts, frameworks or languages, so am I. I learned to program with Android 2.0. Yes, I’m getting old… Meanwhile, I am not so active in the Android world anymore. But since we code Android apps on hackathons every now and then, I don’t get out of practice. Nevertheless, I am a little spoiled by the development of UWP apps. Also, the time on hackathons is very limited, which is why one does not like to deal with boilerplate code. Let’s be honest… Android (Java) is full of it! Therefore, alternatives are needed. Okay, so it would be obvious to try Xamarin now, but where would be the fun? I have no plans this weekend and Google seems to have created a real alternative with Flutter and Dart! Sounds like a hot date ? I’ve never worked with Dart or Flutter before, but at some point, you have to start. So this post will be about my experiences and not a tutorial.<\/p>\n
Google promises not only that you can develop apps very fast, but also that they run on iOS like Android. In addition, Dart and Flutter are focused on developing reactive apps. I’ve never written apps in a reactive manner before, but at least the theoretical foundations were covered at our university. *Yes, Dannymausi I’m talking about the hipster teacher \ud83d\ude09 *<\/p>\n
I’ve been thinking for a long time what I can achieve in less than two days (hitting the gym and watching Netflix is still important!). I don’t know the language or the framework…. But yolo , after all, Google promises that you are significantly<\/strong> faster in developing. So here’s the idea: We’ve finished the first rotation of our blog posts. We came up with the idea that once every member of us has written a blog post we select the best and the winner gets a little something. Basically, this means we have to make a voting every 5 to 6 weeks. There is one catch: Since Danny is from Russia we have to very be very cautious about our voting system. We had so many different voting systems… I can’t even count them! We even voted about how to vote… Time to time we vote with two or three voting shares. Sometimes we have several voting rounds. Since we always discuss and try to optimize our voting systems, it’s time for an App. The app should allow you to vote for one of the five possible authors (including yourself). After you have voted, the application navigates to a second page where you can see the election results. You can not vote again. However, a new election can be started, this must be propagated to all clients.<\/p>\n So now I have two days to try out a new language and a new framework….. What the hell am I doing to myself ?? So that it won’t be too unpleasant, I decided to chill on YouTube first. Fortunately, the Google IO 18 wasn’t so long ago and there are interesting talks about Flutter. Here are the ones I looked at:<\/p>\n So what should you take out of these videos? On the one hand Flutter is about widgets – everything is a widget. A screen is a widget tree. One distinguishes between Stateless and Statefull widgets. But we will use the BLOC pattern from “Build reactive mobile apps with Flutter” for our app. So this is the easy Part. Since I have already written such a long motivation, how about a gif?<\/p>\n How to create the Backend \ud83d\ude09<\/p><\/div>\n To keep the code manageable I decided to write all widgets in the main.dart file. In a real project you would surely divide them into different files but for experimenting, I think it’s better to keep everything at one place. Since time is limited, I thought we’d keep the layout simple. Here is my design:<\/p>\n Lets Check:<\/p>\n That’s what I call minimalistic \ud83d\ude42 But what is the right place to read from the database? If we follow Google’s BLOC patterns, we need a stream. Using this stream, information from our database will flow straight into our UI. So we don’t have to hold a state. But what if we need information from the database? So not the information from the database flows into the UI but rather the UI pulls information from the database? To solve this elegantly, we would need a short-term memory. Therefore we will let the stream from the Firebase database flow into a so-called BehaviorSubject. This BehaviorSubject can always return the last element seen on request. So it holds our state.<\/p>\n Finally, we need a so-called StreamBuilder in our view. The StreamBuilder builds itself on the latest snapshot interaction with the specified stream an whose build strategy is given by a builder. So lets look at the code:<\/p>\n Here we connect the BlocClass with the Firebase Database alias Firestore<\/p>\n Here we build a widget based on the stream comming from our blocclass But How do we get updates to the Firebase?<\/strong> Actually, it’s pretty easy. We just have to let a stream flow in the opposite direction. For this purpose we create a stream controller in our BlocClass. The StreamController controls our stream. It allows to feed events into the stream via a sink as well as to consume the stream by calling the stream methode. All we have to do is: (1) A Click on the list submits an event to the stream. (2) Consume this stream and start a Firebase transaction at the appearance of a event.<\/p>\n This is all we need in our BlocClass. As you can see the voteSink is the place to commit new Events to our Stream. In the Constructor of this Class, we say that the stream is consumed by a function, which is also defined within the Blocclass. In this function, we start a transaction to safely increment the votes for the author by one. Notice how the actual Author is provided by the vote event itself.<\/p>\n As you can see we just add a new vote event. There is no logic in the UI – all is done by in the BlocClass.<\/p>\n I know the blog post is getting long and I said it won’t be a tutorial. But there’s another detail I want to work out. This has given me the most stuggle and I’m sure there are more elegant ways to solve it. Nevertheless, I would like to share and discuss my solution with you. The challenge I encountered was navigation. Yeah, Flutter’s got a pretty well-designed system for navigation. But what I want is more than the standard requirements. So here a some requirements which I had set for myself:<\/p>\n Well, as you can see, I didn’t make my life easy. One difficulty was to get the BuilderContext from the widget into the blocClass. However, you need the BuilderContext to create a new route. In Flutter Navigation is based on so called “Routes”. A Route is a mapping from a name to a widget. The Navigator managed this routes by adding or removing them from the navigation stack. So first we needed to get the BuildContext to the blocClass. For this we can use a BehavoirSubject again. When we create a widget, we pass the BuildContext as an event. Since a BehavoirSubject always remembers the last element seen, we are always able to navigate. Furthermore we can pass the widget we wish to navigate to into another stream. If we combine both streams, we have everything we need to navigate.<\/p>\n It is important that we do not use BehaviorSubject for the widget stream. We can’t remember the last widget we saw. Otherwise we would end up in an endless navigation. Therefore we use a StreamController for the wigetstream. The BuildContextStream and the WidgetStream are fed into a zipper. The zipper then packs the elements from the two streams into a tuple and the cool thing is: Since we use a BehaviorSubject for the BuildContext, we always get a context here. But since the widget comes via aStreamController, a tuple is only generated if we actually have passed a widget somewhere in our code into the stream.<\/p>\n If you look at the picture from above, the code is quite straightforward. If you want to navigate simply pass the widget to navigate to. For example, when we realize that the voting results have been thrown away:<\/p>\n You’re wondering where this code is coming from again? Easy, actually. Since we already have a stream from the database, we just have to let it flow into this function. With each database update, the system checks whether navigation is required. Time for a gif! That’s been a great two days! Even though I had to fight with a lot of initial pain, I am glad to have chosen this topic. By initial pain, I mean trivial things like spending a lot of time for googling the basics of Dart, being depressed from simple compile errors or even setting up Flutter and Dart on my Computer. Despite still pending refactoring I am also relatively satisfied with the code. Even if I will hate myself for this code in a month \ud83d\ude42 The concept behind Flutter is simply great so far! Personally, I find the development experience much more satisfying than vanilla Android with Java\/Kotlin. I also liked Dart as a programming language very much. I liked the tooling around Flutter, too. By the way, I used Visual Studio code. Feel free to check out the GitHub repo<\/a><\/p>\n","protected":false},"excerpt":{"rendered":" Motivation …Hey, guys! It’s time for another blog post from me. This time I want to write about my experiences with Flutter and Dart. Every software developer is regularly confronted with new concepts, frameworks or languages, so am I. I learned to program with Android 2.0. Yes, I’m getting old… Meanwhile, I am not so active in the Android world … Read More<\/a><\/p>\n","protected":false},"author":8,"featured_media":2362,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[118,120,114],"tags":[],"acf":[],"yoast_head":"\nGetting Started<\/h2>\n
\n
\nOn the other hand, Firebase seems to offer itself perfectly as a backend. As this blog post is not a tutorial, I recommend to watch the videos. Our task is to create a Firebase database. And to create a client according to the BLOC pattern.<\/p>\nImplementation<\/h2>\n
The Backend alias Firebase<\/h3>\n
The Voting Screen<\/h3>\n
<\/p>\n
\n
\nAfter I haven’t committed to any tutorial here, I don’t want to go into the exact structure of the code. Much more interesting is how we build our UI based on the authors in Firebase. First, we have to connect Firebase to our app. Just follow the instructions on Firebase or watch this video<\/a>.<\/p>\nFrom Firebase to Voting<\/h4>\n
<\/p>\n
class Bloc {\r\n final BehaviorSubject<QuerySnapshot> _firebaseSubject =\r\n BehaviorSubject<QuerySnapshot>(seedValue: null);\r\n\r\n Bloc() {\r\n _firebaseSubject.addStream(Firestore.instance.collection(\"AuthorVotes\").snapshots());\r\n }\r\n\r\n Stream<QuerySnapshot> get firebaseStream => _firebaseStream.stream;\r\n }\r\n<\/code><\/pre>\n
class AuthorGrid extends StatelessWidget {\r\n @override\r\n Widget build(BuildContext context) {\r\n final bloc = BlocProvider.of(context);\r\n return new StreamBuilder(\r\n stream: bloc.firebaseStream,\r\n builder: (context, snapshot) {\r\n if (snapshot != null && !snapshot.hasData) return const Text('Loading...');\r\n return ListView.builder(\r\n itemCount: snapshot.data.documents.length,\r\n itemBuilder: (context, index) => \r\n _buildItem(context, snapshot.data.documents[index]), \r\n );\r\n },\r\n );\r\n }\r\n<\/code><\/pre>\n
\nBut wait?!<\/strong> Where comes the BlocClass from? You’re right, the BlocClass itself is also a kind of state we need to have manage. Although the UI of this app is manageable and I could have passed the bloc object through the tree as a parameter, there are better alternatives. With the help of an inheriting widget<\/a> we are able to reach the object from the entire subtree.<\/p>\nFrom Voting to Firebase<\/h4>\n
class Bloc {\r\n final StreamController<Vote> _voteController = StreamController<Vote>();\r\n\r\n Bloc() {\r\n _voteController.stream.listen((data) => commitToFireBase(data));\r\n }\r\n\r\n Sink<Vote> get voteSink => _voteController.sink;\r\n\r\n void commitToFireBase(Vote vote) {\r\n Firestore.instance.runTransaction((transaction) async {\r\n DocumentSnapshot freshSnap = await transaction.get(vote.votedAuthor.reference);\r\n await transaction.update(freshSnap.reference, {'Votes': freshSnap['Votes'] + 1});\r\n });\r\n }\r\n}\r\n<\/code><\/pre>\n
Widget _buildItem(\r\n BuildContext context, DocumentSnapshot document, AuthorBloc authorBloc) {\r\n return new AuthorSquare(\r\n author: document['Author'],\r\n imageUrl: document['Image'],\r\n onTab: () {\r\n bloc.voteSink.add(Vote(document));\r\n },\r\n );\r\n}\r\n<\/code><\/pre>\n
Navigation<\/h4>\n
\n
<\/p>\n
class Bloc {\r\n final StreamController<Widget> _widgetController = StreamController<Widget>();\r\n\r\n final BehaviorSubject<BuildContext> _contextStream =\r\n BehaviorSubject<BuildContext>(seedValue: null);\r\n\r\n AuthorBloc() {\r\n Observable\r\n .zip2(_widgetController.stream, _contextStream.stream,\r\n (a, b) => new Tuple2<Widget, BuildContext>(a, b))\r\n .listen((data) => _navigate(data));\r\n }\r\n\r\n Sink<BuildContext> get contextSink => _contextStream.sink;\r\n Sink<Widget> get wigetSink => _widgetController.sink;\r\n\r\n void _navigate(Tuple2<Widget, BuildContext> tuple) {\r\n Navigator.pushReplacement(tuple.item2,\r\n new MaterialPageRoute(builder: (BuildContext context) => tuple.item1));\r\n }\r\n}\r\n<\/code><\/pre>\n
void _checkForEmptyVotes(QuerySnapshot data) {\r\n int votes =\r\n data.documents.map((snap) => snap.data['Votes']).fold(0, (a, b) => a + b);\r\n\r\n if (votes <= 0) wigetSink.add(new MyHomePage());\r\n}\r\n<\/code><\/pre>\n
\nThe other requirements are now also easy to implement. We simply write in the local settings whether we have already selected. If so, we pass the corresponding widget the stream and already we navigate.<\/p>\nThe Result<\/h2>\n
\n<\/p>\n
\n
Reflection<\/h2>\n
\nWell I will surely change some code and build some model classes. Also I didn’t care about the design yet. But there seems to be still a lot to discover here. After all, Flutter is there to build beautiful apps! I am not yet satisfied with the navigation either. For example, I don’t like my solution, which is waiting for an update of Firebase (also for the device that requested a new voting). But you will certainly read more of me and Flutter ❤<\/p>\n