flutfirebase
My flutter app with firebase
Generate SHA-1 on terminal for android firebase
Go to the project folder in the terminal.
Mac keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
Windows keytool -list -v -keystore “.android\debug.keystore” -alias androiddebugkey -storepass android -keypass android
Linux keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
ends the firbase recommendations and finally the dependencies that will be installed through pubspec.yaml
dependencies : # [recommended] if not a problem if you don't put in your dependency firebase_core : ^1.4.0 cloud_firestore : ^2.4.0 firebase_storage : ^10.0.0
IF it runs for the first time it may give Dex error which means the java codes when compiled generate a .jar file and flutter generates a .dex file and firbase generate larger dex files and k ests are limited from there, to solve this problem this trouble need to settar :
android/app/build.gradle
dependencies {
def multidex_version = "2.0.1"
implementation "androidx.multidex:multidex:$multidex_version"
}
If you aren’t using AndroidX, add the following deprecated support library dependency instead:
dependencies {
implementation 'com.android.support:multidex:1.0.3'
}
and in the same grandle of android/app/ add in defaultConfig that multiDexEnabled=true
android {
defaultConfig {
...
minSdkVersion 15
targetSdkVersion 28
multiDexEnabled true
}
...
}
dependencies {
implementation "androidx.multidex:multidex:2.0.1"
}
Starting the project structure
[-] done the creation of the Getx_pattern structure [+] how will I interact with cloud_firestore [which is a firebase reactive database ] [-] first I will be creating a repository interface that will be able to have all the methods to interact with firebase [fetch the data in the database ] and it’s a reactive bank so I’ll use streams'lib/app/data/repositories/todo_repository_interface.dart' import 'package:flutfirebase/app/data/models/todo_model.dart'; abstract class IFirestoreTodoRepository { /** Interface that will have all the methods to communicate with the firebase reactive database [Cloud_firestore] **/ // as it is a reactive database I will use streams when I get the data Stream < List < TodoModel >> getTodos (); }
and its Repository Class that implements it is
'lib/app/data/repositories/todo_repository.dart' import 'package:flutfirebase/app/data/models/todo_model.dart'; import 'todo_repository_interface.dart'; class FireStoreTodoRepository implements IFirestoreTodoRepository { @override Stream<List<TodoModel>> getTodos() { // TODO: implement getTodos throw UnimplementedError(); } }
'TodoModel the model will have a factory method that in addition to fromJson will have a fromDocumets referring to firestore documents' import 'package:cloud_firestore/cloud_firestore.dart'; class TodoModel { String? uid; String title; bool checked; final DocumentReference? reference; //areferencia TodoModel ( {this.reference, this.uid, required this.title, required this.checked}); factory TodoModel . fromDocumets ( DocumentSnapshot < Map < String , dynamic >> firestore_docus) { // this firestore_docus will appear in json format // hence I will access it with map return new TodoModel ( checked: firestore_docus['checked'], title: firestore_docus['title'], reference: firestore_docus['reference']!); } Map<String, dynamic> toJson() => {'title': title, 'reference': reference!, 'checked': checked}; }
TodoPage Contem dissible Widget
return Scaffold( // appBar: AppBar( // title: Text('ALL TODOS'), // ), resizeToAvoidBottomInset: false, body: SafeArea( child: Column( mainAxisSize : MainAxisSize .min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text('Todos Recentes'), SizedBox(height: 20), // Spacer(), ListView.separated( separatorBuilder: (_, index) => Divider( color: Colors.grey[800], ), shrinkWrap: true, itemCount: 5, itemBuilder: (ctx, index) { <!-- Aplicando dissmisble WIgets --> return Dismissible( background: Container( padding: EdgeInsets.only(left: 20), alignment: Alignment.centerLeft, color: Colors.redAccent, child: Icon( Icons.delete, color: Colors.grey[200], ), ), key: Key(index.toString()), onDismissed: (direction) { switch (direction) { case DismissDirection.startToEnd: { print('removed'); } break; } }, child : ListTile ( onTap: () {}, leading: Container( height: 30, width: 30, // alignment: Alignment.center, // padding: EdgeInsets.all(2), decoration: BoxDecoration( color: Theme.of(ctx).primaryColor, shape: BoxShape.circle, ), child: Icon( Icons.check, color: Colors.white, ), ), title: Text( 'Todo title', style: TextStyle( color: Colors.grey[200], ), ), ), ); }) ], ), ),
on Add new Task open popup
floatingActionButton: FloatingActionButton.extended( onPressed: () { showDialog( context: context, barrierDismissible: false, builder: (_) => SimpleDialog( contentPadding: EdgeInsets.symmetric(horizontal: 25, vertical: 20), backgroundColor: Colors.grey[800], shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), titlePadding: EdgeInsets.all(8), title: Container( padding: EdgeInsets.only(left: 5), decoration: BoxDecoration( color: Theme.of(context).primaryColor.withOpacity(0.4), borderRadius: BorderRadius.circular(10), ), child: Row( children: [ Text( 'Nova Nota', style: TextStyle( color: Colors.white54, ), ), Spacer(), IconButton( icon: Icon(Icons.cancel, color: Theme.of(context).primaryColor), onPressed: () { Get.back(); }), ], ), ), children: [ Divider(), TextFormField( controller: controller.title_edit.value, autofocus: true, maxLines: 2, autofillHints : [ 'saidino' , 'hacker' , 'claudia' , 'Mariamo' ], style: TextStyle(color: Colors.white, height: 1.5, fontSize: 18), decoration: InputDecoration( hintText : 'ex: Programar django' , hintStyle: TextStyle( color: Colors.white30, ), ), ), SizedBox(height: 20), SizedBox( height: 30, width: size.width, child : TextButton ( style: TextButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), ), child: Text( 'Salvar', style: TextStyle( color: Colors.white, fontWeight: FontWeight.w400, ), ), onPressed: () {}, ), ), ], ), ); }, label: Text('create'), icon: Icon(Icons.add), ),
when focussed the dialog
FIREBASFIREESTORE BASICS
full example
A CLASS CONTROLLER DEST WIDGET THAT KEEPS A REFERENCE DO ALL of the non-firestore.
class TodoController extends GetxController { final title_edit = TextEditingController().obs; TodoRepository repository = TodoRepository(); void saveTodo() { if (title_edit.value.text.isNotEmpty) { Get.snackbar( 'Edited', '${title_edit.value.text}', backgroundColor: Colors.grey[800], ); } } Future<void> showData() async { var data = repository.getTodos(); } }
O WIDGET COMPLETO COM AS PARTES DO TODO
@override Widget build(BuildContext context) { var primaryColor = Theme.of(context).primaryColor; return Scaffold( appBar: AppBar( title: TextField( controller: controller.titleEditctl, ), actions: [ IconButton( onPressed: () => Get.toNamed(AppRoutes.TODO), icon: Icon(Icons.file_present), ), ], ),
PARA SER RECUSIVO USAMOS O STREAM BUILDER PARA COMUNICAR EM TEMPO REAL COM BANCO DE DADOS
body: SafeArea( child: StreamBuilder( // stream: FirebaseFirestore.instance.collection('todo').snapshots(), // como ja criei refencia d todo no homeController entao posso usalo stream: controller.todoReference.orderBy('title').snapshots(), builder: (ctx, AsyncSnapshot<QuerySnapshot> snapshot) { if (!snapshot.hasData) return Center(child: CircularProgressIndicator.adaptive()); return ListView( children: snapshot.data!.docs.map((todo) { return Dismissible( background: Container( alignment: Alignment.centerLeft, decoration: BoxDecoration(color: primaryColor), child: Icon( Icons.delete, color: Colors.grey[800], ), ),
ACACO PARA APAGAR DO FIRESTORE
key: Key("${todo['title']}"), onDismissed: (direction) { controller.todoReference.doc(todo.id).delete(); Get.snackbar('Delecao', 'Deletado com sucesso', backgroundColor: Colors.white); }, child: ListTile( title: Text( "${todo['title']} + ${todo['checked']}", style: TextStyle(color: Colors.white30), ),
ACAO PARA ACTUALIZAR NO FIRESTORE
leading: Checkbox( value: todo['checked'].toString().trim().toLowerCase() == 'true' ? true : false, // == 'false'? true : false, //parse(todo['checked']), onChanged: (v) { bool todo_checked = todo['checked'].toString().trim().toLowerCase() == 'true' ? true : false; controller.todoReference .doc(todo.id) .update({'checked': !todo_checked}) .then( (value) => Get.snackbar( 'Updating', 'check updated sucessfull', backgroundColor: Colors.blueAccent, ), ) .catchError((error) { Get.snackbar('Update', 'Erro ao fazer update por favor verifique o database', backgroundColor: primaryColor); }); }, ), onLongPress: () { // Apage ao precionar com alta preesao }, ), ); }).toList(), ); }), ),
ACAO PARA ADICIONAR UM NOVO DADO NO FIRESTORE
floatingActionButton: FloatingActionButton( onPressed: () { if (controller.titleEditctl.text.isNotEmpty) { controller.todoReference.add({ 'title': controller.titleEditctl.text, 'checked': false, }); } controller.titleEditctl.clear(); }, child: Text('Add'), ), ); } }
UTILS [1] HOW TO CREATE ClipPath in Widget
ClipPath( clipper: HomeTopCustomPainter(), child: Container( height: size.height * 0.5, width: double.infinity, decoration: BoxDecoration( gradient: LinearGradient( end: Alignment.bottomLeft, begin: Alignment.topRight, colors: [ Color(0xFF3383CD), Color(0xFF11249F), ])), child: Column( children: [ Container( height: kToolbarHeight, child: Text('Well come'), ), Row( children: [ Padding( padding: const EdgeInsets.all(20.0), child: Container( width: size.width * 0.7, height: 34, child: TextField( style: TextStyle(color: Colors.white), decoration: InputDecoration( filled: true, fillColor: Colors.white24, suffixIcon: Padding( padding: EdgeInsets.all(5), child: Icon(Icons.search), ), // focusedBorder: InputBorder.none, border: OutlineInputBorder( borderSide: BorderSide.none, borderRadius: BorderRadius.circular(20), ), ), ), ), ), Expanded( child: Icon(Icons.scatter_plot_outlined, color: primaryColor), ), ], ) ], ), ), ),
AUTHETICACOA COM GOOGLE SIGN E FIREBASE-AUTH
Para amais explicacoes !()[https://www.youtube.com/watch?v=1k-gITZA9CI]
Neste projecto adicionei as dependencias :
# google sign firebase_auth: ^3.0.1 google_sign_in: ^5.0.5 #Google Icon font_awesome_flutter: ^9.1.0
para tal efeito e isso so sera possivel se adicionar nas configuracoes do porjecto no firebase as duas chaves de sh1 e sh6 que segundo adocumentacao oficial !()[https://developers.google.com/android/guides/client-auth] pode se gerar :
keytool -list -v \ -alias androiddebugkey -keystore ~/.android/debug.keystore password: android Certificate fingerprint: SHA1: DA:39:A3:EE:5E:6B:4B:0D:32:55:BF:EF:95:60:18:90:AF:D8:07:09 Alias: AndroidDebugKey MD5: A5:88:41:04:8D:06:71:6D:FE:33:76:87:AC:AD:19:23 SHA1: A7:89:E5:05:C8:17:A1:22:EA:90:6E:A6:EA:A3:D4:8B:3A:30:AB:18 SHA-256: 05:A2:2C:35:EE:F2:51:23:72:4D:72:67:A5:6C:8C:58:22:2A:00:D6:DB:F6:45:D5:C1:82:D2:80:A4:69:A8:FE Valid until: Wednesday, August 10, 2044 copie as chaves e cole no projecto do firebase clicando na barra de ingrenage de settings (configuracoes do projecto> e para baixo tera icon do app e clique em Adicionar impressa digital)
#Basic Google e FirebaseAUth
/app_controller.dart import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutfirebase/app/ui/utils/shared.dart'; import 'package:google_sign_in/google_sign_in.dart'; class AppController extends GetxController { final googleSignIn = GoogleSignIn (); late GoogleSignInAccount ? _user; Stream get user_auth => FirebaseAuth.instance.authStateChanges(); GoogleSignInAccount? get user => _user; Future googleLogin() async { try { final googleUser = await googleSignIn.signIn(); if (googleUser == null) return; _user = googleUser; final googleAuth = await googleUser.authentication; final credential = GoogleAuthProvider.credential( accessToken: googleAuth.accessToken, idToken : googleAuth.idToken, ); await FirebaseAuth.instance.signInWithCredential(credential); } catch (e) { Get.defaultDialog(title: 'Erro no Login', content: Text('$e')); } if (_user != null) { Get.toNamed(AppRoutes.HOME); } update(); } } # AUth_page Container( padding: const EdgeInsets.all(16), alignment: Alignment.center, child: ElevatedButton.icon( style: ElevatedButton.styleFrom( primary: Colors.white, onPrimary: Colors.black, minimumSize: Size(double.infinity, 50)), label: Text('Use seu email', style: TextStyle(color: Colors.black)), icon: FaIcon(FontAwesomeIcons.google, color: Colors.red), // text: 'Registration', onPressed: () { print('LOgging ...'); app_controller.googleLogin(); print('Loged sucess'); }), ), Container( padding: const EdgeInsets.all(16), alignment: Alignment.center, child: RichText( text: TextSpan(text: ' Ja Tiveste aqui ?', children: [ TextSpan( text : 'Enter' , style: TextStyle(color: Colors.greenAccent)) ]), ), )
Initial page
TO GET THE CREDENTIES OF THE USER LEGATED IN THE APP COM FOTO THE NAME AND SUCH TAL
We INstaciate the User class which comes from the FirebaseAuth.instace.currentUser class!
User? user =FirebaseAuth.instance.currentUser; ou final user =FirebaseAuth.instance.currentUser!; pagendo foto do usuaio CircleAvatar(radius:40,backgroundImage:NetWorkImage(user.photoURL!))
Download Flutter app with firebase app source code on GitHub
Provides the list of the opensource Flutter apps collection with GitHub repository.