13. Conclusion
We have developed the [Flutter] client for the [RdvMedecins] application. For the user, it offers the same screens as the HTML application from the [NestJS] course and as the [Vue.js], [Angular] and [React] from previous courses; however, this is a mobile app installed on the phone, and the same code also generates a web app.
The server, on the other hand, hasn’t changed. Its API and JSON—whose contract (routes, body, codes, and error keys) was sufficient to write the client without ever reading its code—serves both a browser and a phone equally well. There is only one difference: on the phone, there is no browser to store the token cookie, so the app took on that role with just a few lines of code ([CookieClient]). Security remains unchanged: it is still the server that authenticates users, verifies roles and management rules, and decides what a patient is authorized to view.
Readers who have taken the [React] course can compare:
- both are based on the same idea: the interface is a function of the state. A [React] component is a function that returns JSX; a [Flutter] widget has a [build()] method that returns a widget tree. When the state changes ([useState] / [setState]), the interface is rebuilt and then compared to the previous one;
- [React] generates HTML, which the browser renders; [Flutter] renders each pixel itself, using its own engine, on both the phone and in the browser. This results in identical screens across all platforms, but a web application that takes longer to load;
- [Flutter] provides much more than [React]: the widgets from [Material], forms, lists, windows, animations, the theme, and tests. Fewer libraries were required: [go_router] for addresses, [provider] for shared state, and [http] for queries. No CSS: the layout is built using widgets ([Column], [Row], [Padding], [Expanded]...);
- [Dart] is a typed, compiled language with non-nullable types: many errors that [TypeScript] overlooks (a missing [null] value, a misread JSON) are detected at compile time, or as soon as the response is received thanks to [Dart] 3 filtering;
- the trade-off: more verbose screens (the widget tree is deep), and a lifecycle to be familiar with ([initState], [didUpdateWidget], [dispose], [mounted]).
Concepts covered in this course (stateless and stateful widgets, lifecycle, keys, [InheritedWidget] and [provider], [ChangeNotifier], [Future], and [Stream], router and guards, forms, internationalization) are common to any [Flutter] application. For further reading:
- the official documentation, docs.flutter.dev, its tutorials, and its architecture guide (“App architecture”);
- tests: widget tests ([flutter_test]), which interact with a screen without a phone (the screenshots in this document were created using these tests), and integration tests ([integration_test]), which run the actual application on an emulator;
- other widely used state managers: [Riverpod] (the successor to [provider], by the same author), [Bloc];
- Code generation: [json_serializable] and [freezed] generate the methods [fromJson], [copyWith], [==]... from simple declarations;
- secure token storage ([flutter_secure_storage]), notifications, the camera, geolocation: one package per need, on pub.dev;
- iPhone: the same code generates an application [iOS], to be built on a [Mac] using [Xcode].