Blog App-Flutter with Firebase Auth

This is my first project in MEDIUM: so please ignore some errors that I made.

First of all, we have to create a Firebase App in order to do a Blog-App.

In this project, we are going to

:Allow User-Authentication from Firebase

:Create register, login, and splash screens

:Create a user object during the registration process

I have categorised some functions or steps that will make you more easier to understand this project.

Step1: Create Firebase Project

Step2: Enable Firebase Auth

Step3: Integrate Firebase Auth(plugins-install)

Step4: Setting Navigation Flow

Step 1: Create Firebase Project-App

I will show some sample pictures that i have completed in creating Firebase project.

https://youtu.be/6juww5Lmvgo. This link is how to create Firebase Project in Firebase Console.

Step 2: Enable Auth

In first view, you can see set of methods that are basically disabled by default so you may have to enable what kind of authentication you are going to use on your project.

In this step, I am gonna use Email/Password SignIn-SignUp Methods.

If you want to use google SignIn/SignUp method , you have to enable the Google SignIn.

The Default Page, where all the Sign-in methods are disabled.

Enabling the Sign-In method. If you want to use other methods, you have to enable that particular method based on your use.

Step 3: Integrate Auth(plugins-install)

First of all, you need to install this packages that i have mentioned below, in order to manage users and uploading blogs to your project.

firebase_auth is basically used to manage users signIn SignUp. shared_preferences is used to keep the user logging in to the app until he or she signed out. firebase_core and firebase_analytics are for our project to use Firebase as a backend. cloud_firestore is used to upload tasks or blogs in the firebase. uuid is used to name the particular blog tile in id and stored it into the blog cloud collections. modal_bottom_sheet is used for adding blog’s title and description in modern style.

dependencies:
flutter:
sdk: flutter
firebase_auth: ^0.18.0
shared_preferences: ^0.5.7
flutter_svg: ^0.19.0
firebase_core: ^0.5.0+1
firebase_analytics: ^6.0.2
cloud_firestore: ^0.14.1+3
uuid: ^2.2.2
modal_bottom_sheet: ^0.2.2

Step 4 : Navigation Flow

First thing is , I would like to show about my project flow.

Simple Flow

As you can see my project flow , first step is I have to check does the user is already signed in or not. Another step is: if he or she has signed already I have to put him stay logged to the app. And last step is uploading tasks.

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: "Blog-App",
theme: ThemeData(primaryColor: Colors.redAccent),
routes: <String, WidgetBuilder>{
'splashscreen': (BuildContext context) => SplashScreen(),
'login': (BuildContext context) => Login(),
'register':(BuildContext context) => Register(),
'forgotpassword': (BuildContext context) => ForgotPassword(),
'home': (BuildContext context) => Home(),
},
initialRoute: 'splashscreen',
);
}
}

I have set my project initial route to splash-screen as it is common on both sides(authorized and non-authorized).

#1: Splash-Screen-(Code)

import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

class SplashScreen extends StatefulWidget {
@override
SplashScreenState createState() => new SplashScreenState();
}

class SplashScreenState extends State<SplashScreen> with SingleTickerProviderStateMixin {
var _visible = true;

AnimationController animationController;
Animation<double> animation;

startTime() async {
var _duration = new Duration(seconds: 3);
return new Timer(_duration, navigationPage);
}

navigationPage() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String userId = prefs.getString('uid');
if (userId != null) {
Navigator.of(context).pushReplacementNamed('home');
}else{
Navigator.of(context).pushReplacementNamed('login');
}
}

@override
void initState() {
super.initState();
animationController = new AnimationController(vsync: this, duration: new Duration(seconds: 3));
animation = new CurvedAnimation(parent: animationController, curve: Curves.easeOut);

animation.addListener(() => this.setState(() {}));
animationController.forward();

setState(() {
_visible = !_visible;
});
startTime();
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
fit: StackFit.expand,
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(
'assets/logo.png',
width: animation.value * 300,
height: animation.value * 300,
),
],
),
],
),
);
}

}

I have set my splash-screen duration for 3 secs. If you want to increase more time you can change it in the startTime() section. In this screen, I have to check the user has sign-in or not. If not we have to navigate him to the login screen.

if (userId != null) {
Navigator.of(context).pushReplacementNamed('home');
}
else{
Navigator.of(context).pushReplacementNamed('login');
}

#2: Login-Screen(Code)

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uuid/uuid.dart';
class Login extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: LoginScreen(),
);
}
}


class LoginScreen extends StatefulWidget {
@override
_LoginScreenState createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {

double _height, _width;
String _email, _password;
String uid= Uuid().v4();

GlobalKey<FormState> _key = GlobalKey();
bool _showPassword = true, _load = false;
final auth = FirebaseAuth.instance;

@override
Widget build(BuildContext context) {
_height = MediaQuery.of(context).size.height;
_width = MediaQuery.of(context).size.width;

return Scaffold(
appBar: AppBar(title: Text('Login'),),
body: Container(
height: _height,
width: _width,
padding: EdgeInsets.only(bottom: 5),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
image(),
welcomeText(),
loginText(),
form(),
forgetPassText(),
SizedBox(height: _height / 12),
button(),
dontHaveAccount(),
],
),
),
),
);
}

Widget image(){
return Container(
margin: EdgeInsets.only(top: _height / 15.0),
height: 80.0,
width: 100.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
),
child: new Image.asset('assets/login.png'),
);
}

Widget welcomeText(){
return Container(
margin: EdgeInsets.only(left: _width / 20, top: _height / 100),
child: Row(
children: <Widget>[
Text(
"Welcome",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 30,
),
),
],
),
);
}

Widget loginText() {
return Container(
margin: EdgeInsets.only(left: _width / 15.0),
child: Row(
children: <Widget>[
Text(
"Sign in to your account",
style: TextStyle(
fontWeight: FontWeight.w200,
fontSize: 14,
),
),
],
),
);
}

Widget form() {
return Container(
margin: EdgeInsets.only(
left: _width / 15.0,
right: _width / 15.0,
top: _height / 25.0),
child: Form(
key: _key,
child: Column(
children: <Widget>[
emailBox(),
SizedBox(height: _height / 40.0),
passwordBox(),
],
),
),
);
}

Widget emailBox(){
return Material(
borderRadius: BorderRadius.circular(10.0),
elevation: 10,
child:TextFormField(
onSaved: (input) => _email = input,
keyboardType: TextInputType.emailAddress,
cursorColor: Colors.redAccent,
obscureText: false,
decoration: InputDecoration(
prefixIcon: Icon(Icons.email, color: Colors.redAccent, size: 20),
hintText: "Email Id",
border: OutlineInputBorder(
borderSide: BorderSide.none),
),
),
);
}

Widget passwordBox(){
return Material(
borderRadius: BorderRadius.circular(10.0),
elevation: 10,
child:TextFormField(
onSaved: (input) => _password = input,
keyboardType: TextInputType.visiblePassword,
cursorColor: Colors.redAccent,
obscureText: _showPassword,
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock, color: Colors.redAccent, size: 20),
suffixIcon: IconButton(
icon: Icon(
Icons.remove_red_eye,
color: this._showPassword ? Colors.grey : Colors.redAccent,
),
onPressed: () {
setState(() => this._showPassword = !this._showPassword);
},
),
hintText: "Password",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: BorderSide.none
),
),
),
);
}

Widget forgetPassText() {
return Container(
margin: EdgeInsets.only(top: _height / 40.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Forgot your password?",
style: TextStyle(fontWeight: FontWeight.w400,fontSize: 13),
),
SizedBox(
width: 5,
),
GestureDetector(
onTap: () {
print("Routing");
Navigator.of(context).pushNamed('forgotpassword');
},
child: Text(
"Recover",
style: TextStyle(
fontWeight: FontWeight.w600, color: Colors.redAccent),
),
)
],
),
);
}

Widget button() {
return !_load ? RaisedButton(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
onPressed: () async {
RegExp regExp = new RegExp(r'^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$');
final formstate = _key.currentState;
formstate.save();
if(_email == null || _email.isEmpty){
Scaffold.of(context).showSnackBar(SnackBar(content: Text('Email Cannot be empty')));
}else if(_password == null || _password.length < 6){
Scaffold.of(context).showSnackBar(SnackBar(content: Text('Password needs to be atleast six characters')));
}else if(!regExp.hasMatch(_email)){
Scaffold.of(context).showSnackBar(SnackBar(content: Text('Enter a Valid Email')));
}
else{
setState(() {
_load = true;
});
signIn();
}
},
textColor: Colors.white,
padding: EdgeInsets.all(0.0),
child: Container(
alignment: Alignment.center,
width: _width/2,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
gradient: LinearGradient(
colors: <Color>[Colors.orange[200], Colors.pinkAccent],
),
),
padding: const EdgeInsets.all(12.0),
child: Text('SIGN IN',style: TextStyle(fontSize: 15,color: Colors.white)),
),
): Center(
child: CircularProgressIndicator(),
);
}
Widget dontHaveAccount() {
return Container(
margin: EdgeInsets.only(top: _height / 5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Don't have an Account?",
style: TextStyle(fontWeight: FontWeight.w400,fontSize: 13),
),
SizedBox(
width: 5,
),
GestureDetector(
onTap: () {
print("Routing");
Navigator.of(context).pushReplacementNamed('register');
},
child: Text(
"Register",
style: TextStyle(
fontWeight: FontWeight.w600, color: Colors.redAccent),
),
)
],
),
);
}

Future<void>signIn()async{
try{

setState((){_load = true;});
await FirebaseAuth.instance.signInWithEmailAndPassword(email: _email, password: _password).then((_){
Navigator.of(context).pushReplacementNamed('home');
});

final SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('email', _email);
prefs.setString('uid', uid);
setState((){_load = false;});
}
catch(e){
setState((){_load = false;});
print(e.message);
Scaffold.of(context).showSnackBar(SnackBar(content: Text(e.message)));
}
}
}

In login-screen, I have partitioned sections for you to understand more easily and can find which part is appropriate for!

If you are not an authorized user, you may have to register first and then have to login again, to confirm that is that you or not. In Login Page, I have also add forget password function, for special cases, if the user forgot the password, I am sending a link where the user can change or reset his or her password again.

#3: Forget-Password(Code)

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

class ForgotPassword extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: ForgotPasswordScreen(),
);
}
}

class ForgotPasswordScreen extends StatefulWidget {
@override
_ForgotPasswordScreenState createState() => _ForgotPasswordScreenState();
}

class _ForgotPasswordScreenState extends State<ForgotPasswordScreen> {
double _height, _width;
String _email = '';
bool _load = false;

GlobalKey<FormState> _key = GlobalKey();

@override
Widget build(BuildContext context) {

_height = MediaQuery.of(context).size.height;
_width = MediaQuery.of(context).size.width;

return Scaffold(
appBar: AppBar(
title: Text('Forgot Password'),
),
body: Container(
height: _height,
width: _width,
padding: EdgeInsets.only(bottom: 5),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
image(),
form(),
SizedBox(height: _height / 12),
button()
],
),
),
),
);
}

Widget image(){
return Container(
margin: EdgeInsets.only(top: _height / 15.0),
height: 100.0,
width: 100.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
),
child: new Image.asset('assets/login.png'),
);
}

Widget form() {
return Container(
margin: EdgeInsets.only(
left: _width / 12.0,
right: _width / 12.0,
top: _height / 15.0),
child: Form(
key: _key,
child: Column(
children: <Widget>[
emailBox(),
],
),
),
);
}

Widget emailBox(){
return Material(
borderRadius: BorderRadius.circular(30.0),
elevation: 10,
child:TextFormField(
onSaved: (input) => _email = input,
keyboardType: TextInputType.emailAddress,
cursorColor: Colors.redAccent,
obscureText: false,
decoration: InputDecoration(
prefixIcon: Icon(Icons.email, color: Colors.redAccent, size: 20),
hintText: "Email Id",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: BorderSide.none),
),
),
);
}

Widget button() {
return !_load ? RaisedButton(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30.0)),
onPressed: (){
RegExp regExp = new RegExp(r'^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$');
final formstate = _key.currentState;
formstate.save();
if(_email == null || _email.isEmpty){
Scaffold.of(context).showSnackBar(SnackBar(content: Text('Email Cannot be empty')));
}else if(!regExp.hasMatch(_email)){
Scaffold.of(context).showSnackBar(SnackBar(content: Text('Enter a Valid Email')));
}else{
setState((){_load = true;});
resetPassword();
}
},
textColor: Colors.white,
padding: EdgeInsets.all(0.0),
child: Container(
alignment: Alignment.center,
width: _width/2,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20.0)),
gradient: LinearGradient(
colors: <Color>[Colors.orange[200], Colors.pinkAccent],
),
),
padding: const EdgeInsets.all(12.0),
child: Text('Reset Password',style: TextStyle(fontSize: 15)),
),
) : Center(
child: CircularProgressIndicator(),
);
}

Future<void> resetPassword() async {
try{
await FirebaseAuth.instance.sendPasswordResetEmail(email: _email);
setState((){_load = false;});
print("Sending email");
Scaffold.of(context).showSnackBar(SnackBar(content: Text('Reset password link sent to registered email')));
}catch(e){
setState((){_load = false;});
print(e.message);
Scaffold.of(context).showSnackBar(SnackBar(content: Text(e.message)));
}
}

}

#4: Register-Screen(Code)

import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uuid/uuid.dart';

class Register extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: RegisterScreen(),
);
}
}


class RegisterScreen extends StatefulWidget {
@override
_RegisterScreenState createState() => _RegisterScreenState();
}

class _RegisterScreenState extends State<RegisterScreen> {

double _height, _width;
String _email, _password;
GlobalKey<FormState> _key = GlobalKey();
bool _showPassword = true, _load = false;
String uid= Uuid().v4();

@override
Widget build(BuildContext context) {
_height = MediaQuery.of(context).size.height;
_width = MediaQuery.of(context).size.width;

return Scaffold(
appBar: AppBar(title: Text('Register'),),
body: Container(
height: _height,
width: _width,
padding: EdgeInsets.only(bottom: 5),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
image(),
welcomeText(),
loginText(),
form(),
SizedBox(height: _height / 12),
button(),
dontHaveAccount(),
],
),
),
),
);
}

Widget image(){
return Container(
margin: EdgeInsets.only(top: _height / 15.0),
height: 80.0,
width: 100.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
),
child: new Image.asset('assets/images/login.png'),
);
}

Widget welcomeText(){
return Container(
margin: EdgeInsets.only(left: _width / 20, top: _height / 100),
child: Row(
children: <Widget>[
Text(
"Create An Account",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 30,
),
),
],
),
);
}

Widget loginText() {
return Container(
margin: EdgeInsets.only(left: _width / 15.0),
child: Row(
children: <Widget>[
Text(
"Sign up for free!",
style: TextStyle(
fontWeight: FontWeight.w200,
fontSize: 14,
),
),
],
),
);
}

Widget form() {
return Container(
margin: EdgeInsets.only(
left: _width / 15.0,
right: _width / 15.0,
top: _height / 25.0),
child: Form(
key: _key,
child: Column(
children: <Widget>[
emailBox(),
SizedBox(height: _height / 40.0),
passwordBox(),
],
),
),
);
}

Widget emailBox(){
return Material(
borderRadius: BorderRadius.circular(10.0),
elevation: 10,
child:TextFormField(
onSaved: (input) => _email = input,
keyboardType: TextInputType.emailAddress,
cursorColor: Colors.redAccent,
obscureText: false,
decoration: InputDecoration(
prefixIcon: Icon(Icons.email, color: Colors.redAccent, size: 20),
hintText: "Email Id",
border: OutlineInputBorder(
borderSide: BorderSide.none),
),
),
);
}

Widget passwordBox(){
return Material(
borderRadius: BorderRadius.circular(10.0),
elevation: 10,
child:TextFormField(
onSaved: (input) => _password = input,
keyboardType: TextInputType.visiblePassword,
cursorColor: Colors.redAccent,
obscureText: _showPassword,
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock, color: Colors.redAccent, size: 20),
suffixIcon: IconButton(
icon: Icon(
Icons.remove_red_eye,
color: this._showPassword ? Colors.grey : Colors.redAccent,
),
onPressed: () {
setState(() => this._showPassword = !this._showPassword);
},
),
hintText: "Password",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(30.0),
borderSide: BorderSide.none
),
),
),
);
}

Widget forgetPassText() {
return Container(
margin: EdgeInsets.only(top: _height / 40.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Forgot your password?",
style: TextStyle(fontWeight: FontWeight.w400,fontSize: 13),
),
SizedBox(
width: 5,
),
GestureDetector(
onTap: () {
print("Routing");
Navigator.of(context).pushNamed('forgotpassword');
},
child: Text(
"Recover",
style: TextStyle(
fontWeight: FontWeight.w600, color: Colors.redAccent),
),
)
],
),
);
}

Widget button() {
return !_load ? RaisedButton(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),

onPressed: () async {
RegExp regExp = new RegExp(r'^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$');
final formstate = _key.currentState;
formstate.save();
if(_email == null || _email.isEmpty){
Scaffold.of(context).showSnackBar(SnackBar(content: Text('Email Cannot be empty')));
}else if(_password == null || _password.length < 6){
Scaffold.of(context).showSnackBar(SnackBar(content: Text('Password needs to be atleast six characters')));
}else if(!regExp.hasMatch(_email)){
Scaffold.of(context).showSnackBar(SnackBar(content: Text('Enter a Valid Email')));
}
else{
setState((){_load = true;});
signUp();
}
},
textColor: Colors.white,
padding: EdgeInsets.all(0.0),
child: Container(
alignment: Alignment.center,
width: _width/2,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
gradient: LinearGradient(
colors: <Color>[Colors.orange[200], Colors.pinkAccent],
),
),
padding: const EdgeInsets.all(12.0),
child: Text('SIGN UP',style: TextStyle(fontSize: 15,color: Colors.white)),
),
): Center(
child: CircularProgressIndicator(),
);
}

Widget dontHaveAccount() {
return Container(
margin: EdgeInsets.only(top: _height / 5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"Already Registered?",
style: TextStyle(fontWeight: FontWeight.w400,fontSize: 13),
),
SizedBox(
width: 5,
),
GestureDetector(
onTap: () {
print("Routing");
Navigator.of(context).pushReplacementNamed('login');
},
child: Text(
"Login",
style: TextStyle(
fontWeight: FontWeight.w600, color: Colors.redAccent),
),
)
],
),
);
}

Future<void> signUp() async {
try{

setState((){_load = true;});
await FirebaseAuth.instance.createUserWithEmailAndPassword(email: _email, password: _password).then((_) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('email', _email);
prefs.setString('uid', uid);

setState((){_load = false;});
Navigator.of(context).pushReplacementNamed('login');
});

}
catch(e){
setState((){_load = false;});
print(e.message);
Scaffold.of(context).showSnackBar(SnackBar(content: Text(e.message)));
}
}

}

In register-screen, we have to register our mail and password in order to login to use the app. The email you be valid and the password length should be more than 6.

#5: Home_Screen(Code)

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_blog/models/blogs.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uuid/uuid.dart';

class Home extends StatelessWidget {

@override
Widget build(BuildContext context) {
return Scaffold(
body: HomeScreen(),
);
}
}

class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {


String blogId= Uuid().v4();
String title;
String desc;

SharedPreferences prefs;
bool _load = false;
@override
void initState(){
super.initState();
try {
SharedPreferences.getInstance().then((sharedPrefs) {
setState(() {
prefs = sharedPrefs;
String _uid = prefs.getString('uid');
});
});
} catch (e) {
print(e.message);
Scaffold.of(context).showSnackBar(SnackBar(content: Text(e.message)));
}

}
Widget ModernBottomSheet(){
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(alignment: Alignment.topLeft,child: Text('Brief Data',style: TextStyle(fontSize: 18.0,fontWeight: FontWeight.bold),)),
SizedBox(height: 10.0,),
TextFormField(
validator: (val) => val.isEmpty ? "Enter item name" : null,
decoration: InputDecoration(
hintText: "Blog Title",
prefixIcon: Icon(Icons.title,size: 25.0,),
border: OutlineInputBorder(),
),
onChanged: (val){
title= val;
},
),
SizedBox(height: 5.0,),
TextFormField(
keyboardType: TextInputType.name,
validator: (val) => val.isEmpty ? "Enter item price" : null,
decoration: InputDecoration(
hintText: "Blog Description",
prefixIcon: Icon(Icons.description,size: 25.0,),
border: OutlineInputBorder(),
),
onChanged: (val){
desc= val;
},
),

SizedBox(height: 5.0,),
GestureDetector(
onTap: (){
if (title == null ||
title.trim().length == 0) {
} else if (desc == null) {
} else {
Firestore.instance
.collection('blogs')
.document(blogId)
.setData({
'title': title,
'blogId': blogId,
'desc': desc,
});

setState(() {
blogId = Uuid().v4();
title = '';
desc = '';
});
Navigator.pop(context);
}
},
child: Container(
alignment: Alignment.center,
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.all(20.0),
decoration: BoxDecoration(
color: Colors.redAccent,
borderRadius: BorderRadius.circular(5.0)
),
child: Text(
"Add Blogs",
style: TextStyle(
fontSize: 16, color: Colors.white),
),
),
)
],
),
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Blog-App'),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.exit_to_app,
color: Colors.white,
),
onPressed: (){
setState((){_load = true;});
logout();
},
)
],
),

body: Column(
children: [
Container(
margin: EdgeInsets.only(top: 10.0,left: 10.0,right: 10.0),
child: FlatButton(
height: 50.0,
color: Colors.redAccent,
onPressed: () {
showModalBottomSheet(
isScrollControlled: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.0),
topRight: Radius.circular(20.0))),
context: context,
builder: (BuildContext context) {
return ModernBottomSheet();
});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 40.0,
height: 40.0,
decoration: BoxDecoration(

borderRadius: BorderRadius.circular(50.0),
//color: Colors.white
),
child: Icon(
Icons.add,
size: 25.0,
color: Colors.white,
),
),
SizedBox(width: 10.0,),
Container(
child: Text('Add Clients',style: TextStyle(fontSize: 14.0,color: Colors.white),),
)
],
),
),
),

Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('blogs').snapshots(),
builder: (context,snapshot){
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator(
backgroundColor: Colors.redAccent,
),);
}
List<BlogTile> customerTiles = [];
snapshot.data.documents.forEach((doc){
BlogModel blogModel = BlogModel.fromDocument(doc);
customerTiles.add(BlogTile(blogModel));
});
if(customerTiles.length==0){
return Center(
child: Column(
children: [
SizedBox(height: 10.0,),
Icon(
Icons.info_outline_rounded
),
SizedBox(
height: 20.0,
),
Text('No Blogs')
],
),
);
}
return ListView(
children: customerTiles,
);
},
),
),
],
),
);
}
void logout() async {
try{
await FirebaseAuth.instance.signOut();
final SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('email', null);
prefs.setString('uid', null);
setState((){_load = false;});
Navigator.of(context).pushReplacementNamed('login');
}catch(e){
setState((){_load = false;});
print(e.message);
Scaffold.of(context).showSnackBar(SnackBar(content: Text(e.message)));
}
}
}
class BlogTile extends StatefulWidget {
final BlogModel blogModel;

const BlogTile(this.blogModel);
@override
_BlogTileState createState() => _BlogTileState();
}

class _BlogTileState extends State<BlogTile> {
@override
Widget build(BuildContext context) {
return Container(
height: 80.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
boxShadow: [
BoxShadow(
offset: Offset(0,0),
color: Colors.redAccent.withOpacity(.5)
)
]
),
margin: EdgeInsets.all(10.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
child: Text(
widget.blogModel.title,
style: TextStyle(
color: Colors.black,fontSize: 16.0,
fontWeight: FontWeight.w600
),
),
),
Container(
child: Text(
widget.blogModel.desc,
style: TextStyle(
color: Colors.black,fontSize: 14.0,
fontWeight: FontWeight.w400
),
),
)
],
),
);
}
}

In Home-Screen, We have a log-out button and a blog uploader to upload and save our blogs in the firebase firestore.

Mobile Dev. I have experienced more than 2 years in flutter and had created some real time app and social apps. May this project will help you.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store