Skip to content

Fireship.io tutorial

To create new project

flutter create --org com.wildetechsolutions myapp

To check flutters compatibility with all build modes, run

flutter doctor --verbose

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

auth.dart
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

npm install @google-cloud/firestore

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

flutter pub run build_runner build

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>>

Topics Screen build function
@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.

Comments