Flutter App,What is Flutter?
Flutter App,Flutter is Google’s cell app development SDK that lets in your product to target both Android and iOS systems concurrently, without the want to maintain separate codebases.
Furthermore, apps the usage of Flutter also can be compiled to goal Google’s upcoming Fuchsia running machine.
Flutter lately hit a major milestone – solid model 1.Zero. The launch took place in London, December fifth, 2018, at the Flutter Live event.
While it may nonetheless be appeared as an early and evolving software undertaking, this newsletter will consciousness on an already demonstrated concept.
display how to expand a fully-purposeful messaging app that objectives both foremost cell systems the usage of Flutter 1.2 and Firebase.
As can be visible from the chart under, Flutter has been gaining a whole lot of customers in latest months.
In 2018, Flutter’s market share doubled and it’s miles on target to surpass React Native in phrases of seek queries, as a result our decision to create a brand new Flutter academic.
Note:This article focuses simplest on positive bits of the implementation. Full supply code reference for the project can be determined in this GitHub repo.
Flutter App,Prerequisites
Flutter App,Even although effort has been made to permit readers to follow and achieve this assignment even supposing it’s their first attempt at cell development,
loads of core cellular improvement standards that aren’t Flutter-unique are noted and used without specific explanation.
This has been undertaken for article brevity as one in every of its objectives is for the reader to complete the venture in a single sitting.
Finally, the object assumes you have already got your improvement surroundings set up, which includes the specified Android Studio plugins and Flutter SDK.
Flutter App,Firebase Set Up
Flutter App,Setting up Firebase is the best thing that we need to do independently for each platform.
First of all, make sure you create a brand new challenge in the Firebase Dashboard.
upload Android and iOS applications inside the newly generated workspace.
The platform will produce configuration documents which you need to down load: google-offerings.Json for Android and GoogleService-Info.Plist for iOS. Before closing the dashboard, make certain to allow Firebase and Google authentication companies as we’ll use them for consumer identity. To do this, select the Authentication item from the menu and then choose the Sign-In technique tab.
Now you may close the dashboard as the rest of the installation takes location in our codebase. First of all, we need to put the documents we downloaded in our task. The google-offerings.Json record have to be located within the $(FLUTTER_PROJECT_ROOT)/android/app folder and GoogleService-Info.Plist should be positioned in the $(FLUTTER_PROJECT_ROOT)/ios/Runner listing.
Next, we need to really set up the Firebase libraries we’re going to apply within the undertaking and hook them up with the configuration documents.
This is done with the aid of specifying the Dart packages (libraries) we’ll be the use of in our assignment’s pubspec.Yaml record.
In the dependencies section of the record, paste the following snippet:
flutter_bloc:
shared_preferences:
firebase_auth:
cloud_firestore:
google_sign_in:
flutter_facebook_login:
The first two aren’t associated with Firebase but are going to be frequently used in the project. The ultimate are, optimistically, self-explanatory.
Finally, we need to configure platform-precise assignment settings with the intention to allow our authentication go with the flow to complete efficiently.
On the Android facet, we want to add the google-offerings Gradle plugin to our undertaking-level Gradle configuration. In different words, we need to feature the following item to the dependency list in file:
$(FLUTTER_PROJECT_ROOT)/android/build.gradle
classpath 'com.google.gms:google-services:4.2.0' // change 4.2.0 to the latest version
Then we need to apply that plugin by adding this line to the end of
$(FLUTTER_PROJECT_ROOT)/android/app/build.gradle
apply plugin: 'com.google.gms.google-services'
The remaining component for this platform is to enlist your Facebook utility parameters. What we’re searching out right here is enhancing those files –
$(FLUTTER_PROJECT_ROOT)/android/app/src/main/AndroidManifest.xml
$(FLUTTER_PROJECT_ROOT)/android/app/src/main/res/values/strings.xml
<!-- AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android>
<!-- … -->
<application>
<!-- … -->
<meta-data android:name="com.facebook.sdk.ApplicationId"
android:value="@string/facebook_app_id"/>
<activity
android:name="com.facebook.FacebookActivity"
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:label="@string/app_name" />
<activity
android:name="com.facebook.CustomTabActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="@string/fb_login_protocol_scheme" />
</intent-filter>
</activity>
<!-- … -->
</application>
</manifest>
<!-- strings.xml -->
<resources>
<string name="app_name">Toptal Chat</string>
<string name="facebook_app_id">${YOUR_FACEBOOK_APP_ID}</string>
<string name="fb_login_protocol_scheme">${YOUR_FACEBOOK_URL}</string>
</resources>
Now it’s time for iOS. Luckily, we simplest want to trade one file in this example. Add the subsequent values (observe that CFBundleURLTypes object may also already exist in the list; if so, you want to feature these gadgets to the prevailing array as opposed to affirming it once more) to
$(FLUTTER_PROJECT)ROOT/ios/Runner/Info.plist
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>${YOUR_FACEBOOK_URL}</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>${YOUR_REVERSED_GOOGLE_WEB_CLIENT_ID}</string>
</array>
</dict>
</array>
<key>FacebookAppID</key>
<string>${YOUR_FACEBOOK_APP_ID}</string>
<key>FacebookDisplayName</key>
<string>${YOUR_FACEBOOK_APP_NAME}</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>fbapi</string>
<string>fb-messenger-share-api</string>
<string>fbauth2</string>
<string>fbshareextension</string>
</array>
A Word on BLoC Architecture
This structure standard turned into described in one of our previous articles, \
demonstrating the use of BLoC for code sharing in Flutter and AngularDart, so we won’t be explaining it in element here.
The basic concept at the back of the principle concept is that each display has the following instructions: – view – that is answerable for showing the cutting-edge kingdom,
delegating consumer input as events to bloc. – state – which represents “stay” facts that the user interacts with using the cutting-edge view.
– bloc – which responds to occasions and updates the country therefore,
optionally requesting facts from one or many nearby or faraway repositories.
– event – that’s a precise action end result which can or may not alternate the modern nation.
As a photo illustration, it may be notion of like this:
Additionally, we have a model directory which contains data classes and repositories that produce instances of these classes.
UI Development
Creating UI the use of Flutter is carried out absolutely in Dart, instead of native app improvement in Android.
iOS wherein the UI is constructed using the XML scheme and is completely separated from the commercial enterprise logic codebase.
We’re going to apply fantastically easy UI detail compositions with special additives primarily based at the present day state (e.G. IsLoading, isEmpty parameters). The UI in Flutter revolves around widgets, or instead the widget tree. Widgets can either be stateless or stateful.
When it comes to stateful ones, it’s critical to strain that, while setState() is called on a selected widget this is currently displayed (calling it in the constructor or after it’s disposed outcomes in a runtime error),
a construct and draw skip is scheduled to be accomplished on subsequent drawing cycle.
For brevity, we’ll best display one of the UI (view) instructions right here:
class LoginScreen extends StatefulWidget {
LoginScreen({Key key}) : super(key: key);
@override
State<StatefulWidget> createState() => _LoginState();
}
class _LoginState extends State<LoginScreen> {
final _bloc = LoginBloc();
@override
Widget build(BuildContext context) {
return BlocProvider<LoginBloc>(
bloc: _bloc,
child: LoginWidget(widget: widget, widgetState: this)
);
}
@override
void dispose() {
_bloc.dispose();
super.dispose();
}
}
class LoginWidget extends StatelessWidget {
const LoginWidget({Key key, @required this.widget, @required this.widgetState}) : super(key: key);
final LoginScreen widget;
final _LoginState widgetState;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Login"),
),
body: BlocBuilder(
bloc: BlocProvider.of<LoginBloc>(context),
builder: (context, LoginState state) {
if (state.loading) {
return Center(
child: CircularProgressIndicator(strokeWidth: 4.0)
);
} else {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
ButtonTheme(
minWidth: 256.0,
height: 32.0,
child: RaisedButton(
onPressed: () => BlocProvider.of<LoginBloc>(context).onLoginGoogle(this),
child: Text(
"Login with Google",
style: TextStyle(color: Colors.white),
),
color: Colors.redAccent,
),
),
ButtonTheme(
minWidth: 256.0,
height: 32.0,
child: RaisedButton(
onPressed: () => BlocProvider.of<LoginBloc>(context).onLoginFacebook(this),
child: Text(
"Login with Facebook",
style: TextStyle(color: Colors.white),
),
color: Colors.blueAccent,
),
),
],
),
);
}
}),
);
}
void navigateToMain() {
NavigationHelper.navigateToMain(widgetState.context);
}
}
The rest of the UI classes follow the same patterns but perhaps have different actions.
might feature an empty state widget tree in addition to loading state.
Authentication
As you could have guessed, we’ll be the use of google_sign_in.
flutter_facebook_login libraries to authenticate the user by counting on their social network profile.
First of all, make certain to import those programs into the file that’s going to address the login request common sense:
import 'package:flutter_facebook_login/flutter_facebook_login.dart';
import 'package:google_sign_in/google_sign_in.dart';
Now, we’re going to have two independent parts that are going to take care of our authentication flow. The first one is going to initiate either a Facebook or Google sign-in request:
void onLoginGoogle(LoginWidget view) async {
dispatch(LoginEventInProgress());
final googleSignInRepo = GoogleSignIn(signInOption: SignInOption.standard, scopes: ["profile", "email"]);
final account = await googleSignInRepo.signIn();
if (account != null) {
LoginRepo.getInstance().signInWithGoogle(account);
} else {
dispatch(LogoutEvent());
}
}
void onLoginFacebook(LoginWidget view) async {
dispatch(LoginEventInProgress());
final facebookSignInRepo = FacebookLogin();
final signInResult = await facebookSignInRepo.logInWithReadPermissions(["email"]);
if (signInResult.status == FacebookLoginStatus.loggedIn) {
LoginRepo.getInstance().signInWithFacebook(signInResult);
} else if (signInResult.status == FacebookLoginStatus.cancelledByUser) {
dispatch(LogoutEvent());
} else {
dispatch(LoginErrorEvent(signInResult.errorMessage));
}
}
The 2nd one is going to be referred to as while we get the profile information from either provider. We’re going to perform this via instructing our login handler to concentrate to firebase_auth onAuthStateChange flow:
void _setupAuthStateListener(LoginWidget view) {
if (_authStateListener == null) {
_authStateListener = FirebaseAuth.instance.onAuthStateChanged.listen((user) {
if (user != null) {
final loginProvider = user.providerId;
UserRepo.getInstance().setCurrentUser(User.fromFirebaseUser(user));
if (loginProvider == "google") {
// TODO analytics call for google login provider
} else {
// TODO analytics call for facebook login provider
}
view.navigateToMain();
} else {
dispatch(LogoutEvent());
}
}, onError: (error) {
dispatch(LoginErrorEvent(error));
});
}
}
The UserRepo and LoginRepo implementation will now not be posted right here, however feel loose to test the GitHub repo for complete reference.
Flutter Tutorial: How to Build an Instant Messaging App
Finally, we get to the thrilling part. As the name implies, the messages should be exchanged as fast as viable, ideally, this ought to be immediately.
Luckily, cloud_firestore permits us to engage with Firestore example and we are able to use its snapshots() feature to open a records flow with the intention to deliver us updates in real time.
In my opinion, all the chat_repo code is quite trustworthy excluding the startChatroomForUsers approach.
It is accountable for growing a new chat room for two users until there’s an existing one which consists of each customers,
(as we don’t need to have multiple times of the identical consumer pair) in which case it returns the prevailing chat room.
However, due to the layout of Firestore, it presently doesn’t aid nested array-consists of queries. So we cannot retrieve the suitable information circulate but need to perform extra filtering on our aspect.
That solution consists of retrieving all the chatrooms for the logged in person and then searching for the one that also carries the selected person:
How to Build an Instant Messaging App
Future<SelectedChatroom> startChatroomForUsers(List<User> users) async {
DocumentReference userRef = _firestore
.collection(FirestorePaths.USERS_COLLECTION)
.document(users[1].uid);
QuerySnapshot queryResults = await _firestore
.collection(FirestorePaths.CHATROOMS_COLLECTION)
.where("participants", arrayContains: userRef)
.getDocuments();
DocumentReference otherUserRef = _firestore
.collection(FirestorePaths.USERS_COLLECTION)
.document(users[0].uid);
DocumentSnapshot roomSnapshot = queryResults.documents.firstWhere((room) {
return room.data["participants"].contains(otherUserRef);
}, orElse: () => null);
if (roomSnapshot != null) {
return SelectedChatroom(roomSnapshot.documentID, users[0].displayName);
} else {
Map<String, dynamic> chatroomMap = Map<String, dynamic>();
chatroomMap["messages"] = List<String>(0);
List<DocumentReference> participants = List<DocumentReference>(2);
participants[0] = otherUserRef;
participants[1] = userRef;
chatroomMap["participants"] = participants;
DocumentReference reference = await _firestore
.collection(FirestorePaths.CHATROOMS_COLLECTION)
.add(chatroomMap);
DocumentSnapshot chatroomSnapshot = await reference.get();
return SelectedChatroom(chatroomSnapshot.documentID, users[0].displayName);
}
}
Also, due to similar layout constraints, Firebase currently doesn’t aid array updates (placing new element in an present array subject value) with special FieldValue.ServerTimestamp() price.
This price shows to the platform that the field that contains this rather than an real price need to be populated with the actual timestamp on the server in the intervening time the transaction takes region.
Instead, we’re using DateTime.Now() for the time being we’re growing our new message serialized object and inserting that item into the chat room messages collection
Future<bool> sendMessageToChatroom(String chatroomId, User user, String message) async {
try {
DocumentReference authorRef = _firestore.collection(FirestorePaths.USERS_COLLECTION).document(user.uid);
DocumentReference chatroomRef = _firestore.collection(FirestorePaths.CHATROOMS_COLLECTION).document(chatroomId);
Map<String, dynamic> serializedMessage = {
"author" : authorRef,
"timestamp" : DateTime.now(),
"value" : message
};
chatroomRef.updateData({
"messages" : FieldValue.arrayUnion([serializedMessage])
});
return true;
} catch (e) {
print(e.toString());
return false;
}
}
Wrapping Up
Obviously, the Flutter messaging app we advanced is extra of a evidence-of-idea than a marketplace-geared up immediate messaging utility.
As thoughts for in addition development, one might recall introducing stop-to-end encryption.
wealthy content material (institution chats, media attachments, URL parsing).
But before all that, one have to put into effect push notifications as they’re quite much a ought to-have characteristic for an immediate messaging software,
and we’ve moved it out of the scope of this newsletter for the sake of brevity.
Additionally, Firestore remains missing more than one capabilities on the way to have simpler and extra correct data-like nested array-carries queries.
As noted on the begin of the article, Flutter has best lately matured into stable 1.Zero release and goes to maintain developing,
Flutter App,Wrapping Up
no longer most effective in relation to framework functions and competencies,
however also when it comes to the improvement community and 0.33-celebration libraries and assets.
It makes experience to invest your time into getting acquainted with lutter app development now,
because it’s certainly here to live and accelerate your cellular improvement technique.
At no additional fee, Flutter builders will also be geared up to target Google’s rising OS–Fuchsia.