When you decide to build an app seriously, the considerations in your head is far more than the hello-world examples in getting-started docs.
How to create beautiful UI?
How to manage authentication in secure way? (Sign up, log in, log out)
How to store data in secure way?
How to manage states?
How to serialize and deserialize your data?
How to do routing?
How to do localization?
...
If we really wanna do a research of any of the above, it could be long enough to write a book.
Fortunately, there are templates, frameworks, packages that could do the heavy lifting things. But, unfortunately at the same time, they are scattered in the vast sea of Internet, you have to catch them and glue together.
Recently, I glued some of them and found they could work as a good starting point of my new apps.
PART I:
Flutter, the best development framework for my app.
What I am developing is an "App" with rich user-interaction, not a "Website" that full of content.
Flutter enables me to develop a web app that anyone can use it instantly with a browser, bypassed app stores, no need to download and install.
Install Flutter
Only 3 prerequisites are needed:
- Git. Go to Git official Site to download and install it.
- Visual Studio Code (VS Code). Go to VS Code official site to download an install it.
- Flutter extension for VS Code. Go to Flutter extension for VS Code page in VS Code Marketplace to install it.
Use only 1 line of command to make Flutter work
As Flutter's official site tells:
- In VS Code,
Ctrl + Shift + P
to call out Command Palette - Type in
Flutter
- Select
Flutter: New Project
. Follow the guidance.
*DO NOT MISS OUT THESE NOTIFICATIONs: *
Click Download SDK
, then select an appropriate folder to store the SDK.
After the SDK downloaded, THIS NOTIFICATION IS IMPORTANT:
Click Add SDK to PATH
. If succeed, another notification would prompt:
After that, if you still encounter the following notification, just ignore it:
Create a skeleton app
After you finished adding SDK, or you run Flutter: New Project
again, it would let you choose what to create:
I prefer Skeleton Application since it follows some best practices.
If you encounter this prompt, just click yes:
PART II:
Firebase, the best solution of Authentication and Database for my app.
Only 3 prerequisites are needed:
- Node.js 16 or higher
- Java JDK 11 or higher
- Firebase CLI
Install Node.js
In Node.js's official site, it recommend to use fnm
as the node manager.
- Install
fnm
withwinget install Schniz.fnm
-
IMPORTANT: for future convenience, add
fnm
environment configuration command to PowerShell's profile:# Check if $profile exists, if not create it if (-not (Test-Path $profile)) { New-Item -ItemType File -Path $profile -Force } # Check if the line already exists in $profile $lineToAppend = "fnm env --use-on-cd | Out-String | Invoke-Expression" if ((Get-Content $profile) -notcontains $lineToAppend) { Add-Content $profile $lineToAppend }
Enable Powershell script running:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
This allows Powershell to read and execute $profile. Or, you could get a "$profile cannot be loaded" error:
Let
fnm
install and use a nodejs version:fnm use --install-if-missing 20
-
Open another Powershell window to confirm that nodejs is installed by:
node -v # should print `v20.17.0` npm -v # should print `10.8.2`
Install Java JDK
Select "Ready for use JDK xx" to download and install
Install Firebase CLI
Open a Powershell window and run npm install -g firebase-tools
PART III:
Enable Firebase in your Flutter project
- Use VS Code to open your Flutter project (the skeleton project you created in PART I)
- Open Terminal (a Powershell window in VS Code) by
ctrl + `
-
firebase login
This command launches a web browser window, prompting you to sign in to your Google account associated with Firebase. -
firebase init
to setup Firebase in your project######## #### ######## ######## ######## ### ###### ######## ## ## ## ## ## ## ## ## ## ## ## ###### ## ######## ###### ######## ######### ###### ###### ## ## ## ## ## ## ## ## ## ## ## ## #### ## ## ######## ######## ## ## ###### ######## You're about to initialize a Firebase project in this directory: C:\Users\test\Documents\GitHub\test_2 ? Are you ready to proceed? (Y/n)
Y
is capital, means if you hitEnter
, it assumes you inputy
and hitEnter
? Which Firebase features do you want to set up for this directory? Press Space to select features, then Enter to confirm your choices. (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed) ( ) Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance (*) Firestore: Configure security rules and indexes files for Firestore ( ) Functions: Configure a Cloud Functions directory and its files >( ) Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys ( ) Storage: Configure a security rules file for Cloud Storage (*) Emulators: Set up local emulators for Firebase products ( ) Remote Config: Configure a template file for Remote Config (Move up and down to reveal more choices)
I selected
Firestore
andEmulators
. (Authentication
is enabled by default)=== Project Setup First, let's associate this project directory with a Firebase project. You can create multiple project aliases by running firebase use --add, but for now we'll just set up a default project. ? Please select an option: Use an existing project Create a new project Add Firebase to an existing Google Cloud Platform project > Don't set up a default project
For my development purpose, I do not want to actually create a project in my Firebase Console and link it to this project.
I just wanna fast develop my prototype using Firebase Emulator. If I wanna move the code into serious development, I will do
firebase init
again to create a project in Firebase Console, register my app and link it to my project.Here, I choose
Don't set up a default project
? What file should be used for Firestore Rules? (firestore.rules)
Enter
? What file should be used for Firestore indexes? (firestore.indexes.json)
Enter
=== Emulators Setup ? Which Firebase emulators do you want to set up? Press Space to select emulators, then Enter to confirm your choices. (Press <space> to select, <a> to toggle all, <i> to invert selection, and <enter> to proceed) ( ) Eventarc Emulator ( ) Data Connect Emulator ( ) Cloud Tasks Emulator >(*) Authentication Emulator ( ) Functions Emulator (*) Firestore Emulator ( ) Database Emulator (Move up and down to reveal more choices)
I choose
Authentication Emulator
andFirestore Emulator
? Which port do you want to use for the auth emulator? (9099)
Enter
? Which port do you want to use for the firestore emulator? (8080)
Enter
? Would you like to enable the Emulator UI? (Y/n)
Enter
? Which port do you want to use for the Emulator UI (leave empty to use any available port)?
Enter
? Would you like to download the emulators now? (Y/n)
Enter
+ Firebase initialization complete!
PART IV
Add packages and plugins to your Flutter project
Add Firebase packages to Flutter app
firebase_core
package is required for any project:
flutter pub add firebase_core
In PART III, I chose Authentication Emulator
and Firestore Emulator
, in addition to the required , I have to install these two corresponding packages: firebase_auth
and cloud_firestore
:
flutter pub add firebase_auth
flutter pub add cloud_firestore
If you need to install more packages, head to this list and pick them.
Add riverpod
, freezed
packages and their supporting plugins
- Go to
riverpod
official site to learn how to install it - Go to this page to learn what VS Code plugin is recommended to work with
riverpod
package. - Go to
freezed
official page to learn how to install it. - Go to this page to learn what VS Code plugin is recommended to work with
freezed
package.
PART V
Up and running
Run the Flutter app
- Press
F5
to run Flutter app in dev mode:
What I need is Chrome/Edge to run a Web app.
If I decide to develop a Windows/Android native app in future, I will install Visual Studio/Android Intelligence to support encoding.
This is the biggest advantage of Flutter: just focus on one code base! You could build any platform app as long as you put the code base in a corresponding environment!
Run the Firebase Emulators
firebase emulators:start --import=./firebase_emulators_backup --export-on-exit --project=demo-dummy
Let's divide this line of command into parts for better understanding what's going happen:
firebase emulators:start
: This starts the Firebase emulators for the services defined in your firebase.json configuration file.--import=./firebase_emulators_backup
: This option tells the emulators to import data from the specified directory (./firebase_emulators_backup). This is useful for preloading the emulators with a specific state.--export-on-exit
: This option ensures that when the emulators are stopped, they will export their data to the specified directory. This is useful for saving the state of the emulators for future use.--project=demo-dummy
: Specify a project ID starts withdemo-
tells emulated services to use a demo configuration and attempts to access non-emulated services for this project will fail. Remember I just wanna use emulators and did not actually create a real project in Firabase Console? This flag is important.
Emulators are running:
PS C:\Users\test\Documents\GitHub\test_2> firebase emulators:start --import=./firebase_emulators_backup --export-on-exit --project=demo-dummy
i emulators: Starting emulators: auth, firestore
i emulators: Detected demo project ID "demo-dummy", emulated services will use a demo configuration and attempts to access non-emulated services for this project will fail.
i firestore: Firestore Emulator logging to firestore-debug.log
+ firestore: Firestore Emulator UI websocket is running on 9150.
i ui: Emulator UI logging to ui-debug.log
┌─────────────────────────────────────────────────────────────┐
│ ✔ All emulators ready! It is now safe to connect your app. │
│ i View Emulator UI at http://127.0.0.1:4000/ │
└─────────────────────────────────────────────────────────────┘
┌────────────────┬────────────────┬─────────────────────────────────┐
│ Emulator │ Host:Port │ View in Emulator UI │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Authentication │ 127.0.0.1:9099 │ http://127.0.0.1:4000/auth │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Firestore │ 127.0.0.1:8080 │ http://127.0.0.1:4000/firestore │
└────────────────┴────────────────┴─────────────────────────────────┘
Emulator Hub running at 127.0.0.1:4400
Other reserved ports: 4500, 9150
Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.
PART VI
Implementing Riverpod and Freezed
Riverpod
Riverpod makes state management easier. (Why Riverpod?) Here I show you an example of how to manage Firebase's Auth state.
-
Wrap the whole app with
ProviderScope()
... import 'package:flutter_riverpod/flutter_riverpod.dart'; void main() async { runApp( const ProviderScope( child: MainApp(), ) ); }
-
Wrap
FirebaseAuth.instance.authStateChanges();
in a "stream provider".import 'package:firebase_auth/firebase_auth.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'auth.g.dart'; @riverpod Stream<User?> auth(AuthRef ref) { return FirebaseAuth.instance.authStateChanges(); }
Run
dart run build_runner build
in Terminal,auth.g.dart
will be generated. This makes you write the fewest code. -
Listen to the provider
... import 'package:test_1/providers/auth.dart'; class MainApp extends ConsumerWidget { const MainApp({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final AsyncValue<User?> user = ref.watch(authProvider); return MaterialApp( home: user.when( data: (user) { if (user == null) { return LoginPage(); } else { return HomePage(); } }, loading: () => const Scaffold( body: Center( child: CircularProgressIndicator(), ), ), error: (error, stackTrace) { return Scaffold( body: Center( child: Text('Error: $error'), ), ); }, ), ); } }
Freezed
Freezed makes serializing and deserializing really easy.
-
ctrl + shift + P
, inputfreezed
, then selectFreezed: Generate a New Freezed Class
. Input a name, then selectYes
if this Freezed Class should be serializable.I rely to this command. It creates a template as a starting point. (I input "dog" as the Freezed Class name)
import 'package:freezed_annotation/freezed_annotation.dart'; part 'dog.freezed.dart'; part 'dog.g.dart'; @freezed class Dog with _$Dog { factory Dog() = _Dog; factory Dog.fromJson(Map<String, dynamic> json) => _$DogFromJson(json); }
-
All I have to do is add attibutes between the parentheses of
factory Dog() = _Dog;
:... factory Dog( { required String name, required int age, String? color, } ) = _Dog; ...
Run
dart run build_runner build
in Terminal, a 19 lines longdog.g.dart
is generated, and a 192 lines longdog.freezed.dart
is generated. (I can't imagine how to do the hassle without the Freezed package.)
CONCLUSION
Thank you for reading! (Considering that we are already immersed in the era of short video consumption in social media platforms, it is really not easy to patiently read through such a long article.)
The tools I introduced are what I am using in my developing apps. Sure, they are not perfect.
If you wanna share some more awesome tools, leave a comment, or, better, write another long article and paste the link in comment!
Top comments (0)