Fireship.io tutorial¶
To create new project
To check flutters compatibility with all build modes, run
Can write flutter widgets in the browser at https://dartpad.dev/
Most basic element is a Container
, like a div
in html.
Right click on widget, refactor to another type of widget.
Flutter provides row and column widgets.
Stack widget, like a StackPane.
Hero widget can animate between pages.
Future
one-teim async value (single doc read)
Stream
multipel async values (listen to real-time updates)
Stateful widget cons
- sharing data with children
- mixing logic with UI
provider
package helps to separate the business logic from the widget and provide data to stateless widgets throughout the widget tree
Setup¶
So the Firebase setup video does seem a bit out of date, the code is very different than the setup provided at (https://firebase.flutter.dev/docs/overview/). In fact, the setup seems a lot more similar to the process followed on my 12-14 blog post.
Flutter gives two options for routing:
Navigator 1.0 (push and pop screens to a stack)
and
Navigator 2.0 (hasn't been well received)
Theming¶
This has changed a bit from the tutorial and some of it is deprecated. Concepts make sense though in defining custom theme in theme.dart
Home Login¶
Using a stream building, we can allow the authentication state dictate where the app goes from our default home.dart
path
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: AuthService().userStream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text("loading");
} else if (snapshot.hasError) {
return const Center(
child: Text("error"),
);
} else if (snapshot.hasData) {
return const TopicsScreen();
} else {
return const LoginScreen();
}
},
);
}
}
With our initial auth service, the user will not be logged in, and therefore routed to the LoginScreen
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
class AuthService {
final userStream = FirebaseAuth.instance.authStateChanges(); // asynchronous stream to listen to the user's authentication state
final user = FirebaseAuth.instance.currentUser; // synchronous method to get the current user
}
Authentication¶
In the firebase console, go to the project then go to Authentcation. Hi getting started. Go to sign-in method and enable Anonymous
as a provider. This will allow a user to use the app without being registed and we'll still get a user id for them.
Add Google Sign in as a provider on firebase. It will then give you an updated json file to include in the android project. I needed to stop the app, and start it again for the json file to take effect. Hot restart did't work initially. However, after starting the app, the google sign in did work.
Going to skip Apple auth until I set it up on the mac. Note that Apple Auth is required if other social login methods are included.
Database data population¶
After cloning the repository I did need to run
in order for the node scripts to work.
Creating the FireStore database was kind of glossed over. But simply go to the FireStore database and hit create database, I set it to production mode. The node.js project to populate data worked as expected.
Models¶
Bring in json_serializeable
, build_runner
dependency into dev_dependencies
which facilitates serializing/deserializing objects to json. Also bring json_annotation
dependency into the normal dependencies
section.
I added dependency and running flutter pub upgrade --major-versions
to get it up to date (json_serializable: ^6.8.0
at the time of writing)
For each class you'll want to add this code
factory Topic.fromJson(Map<String, dynamic> json) => _$TopicFromJson(json);
Map<String, dynamic> toJson() => _$TopicToJson(this);
Now from the terminal you can just run the following to generate the mappers
Building UI¶
For dynamic UI rendering based on a returned api response, we can use FutureBuilder
which takes the future
as an argument and then a builder function which can return the widget based on the result of the future. You can also strongly type the FutureBuilder
, i.e. FutureBuilder<List<Topic>>
@override
Widget build(BuildContext context) {
return FutureBuilder<List<Topic>>(
future: FirestoreService().getTopics(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const LoadingScreen();
} else if (snapshot.hasError) {
return Center(
child: ErrorMessage(message: snapshot.error.toString()),
);
} else if (snapshot.hasData) {
var topics = snapshot.data!;
return ...[widget];
} else {
return const Text('No topics found in Firestore. Check database');
}
},
);
}
Navigator does not necessarily have to navigate to a route, it can navigate to a widget as well
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) => TopicScreen(topic: topic),
),
);
Provider
Provider is a very popoluate flutter library, easier to share values throughout the widget tree. We'll want this to listen to the report stream from multiple widgets. We could listen to it with a StreamBuilder for every page/widget, but that wouldn't be very efficient.