[By the docks] Flutter. Part 1. For Android developers
Flutter has already written many articles. Every month it is becoming more popular. So I decided to interpret the official Flutter documentation in a concise Q & A format. I think many, like me, do not have enough free time to study in detail the documentation of the framework with which they still do not work.
If you want to understand what this framework is good for, and to evaluate how much effort you will have to make in order to use it, welcome to cat.
What is the equivalent of View in Flutter?
Widget
View - in fact, what will be on the screen. Invalidate () is called to display the changes.
Widget - a description of what will be on the screen. For change is created anew.
When launched on Android itself, View is under the hood of Widget. Flutter includes the Material Components library . It contains widgets that implement Material Design guidelines .
How to update the display of widgets?
Using StatefulWidget and its State . Flutter has 2 kinds of widgets: StatelessWidget and StatefulWidget . They work the same way, the only difference is in the rendering state.
StatelessWidget has an immutable state. Suitable for displaying text, logo, etc. Those. if the element on the screen should not change during the entire display time, then it suits you. It can also be used as a container for stateful widgets.
StatefulWidget has the State state, which stores information about the current state. If you want to change an element on the screen when performing some action (a response came from the server, the user clicked on a button, etc.) - this is your option.
1) StatelessWidget - Text
2) StatefulWidget - when you click on the (FloatingActionButton) button, the text in the Text widget changes from “I Like Flutter” to “Flutter is Awesome!”.
How to layout a screen with widgets? Where is the XML layout file?
Flutter has no XML layout for screens. Everything typeset in the widget tree directly in the code.
All default widgets in Flutter can be viewed in the widget catalog .
How to add or remove a component in layout while the application is running?
Through a function that will return the desired widget depending on the state.
On Android, you can do addView () or removeView () in a ViewGroup. In Flutter it’s not possible, because widgets are unchanged. Only their condition can change.
How to change Text to Button by clicking on FloatingActionButton.
How to animate widgets?
Using the AnimationController class , which is the descendant of the Animation abstract class . In addition to starting the animation, he can pause it, rewind, stop and play it in the opposite direction. Works with Ticker , which reports a screen redraw.
On Android, you can create animations in XML or animate View using animate (). In Flutter, animation must be written in code using the AnimationController.
You can learn more in the Animation & Motion widgets , Animations tutorial, and Animations overview .
Fade animation of the Flutter logo.
How to use Canvas ?
Android and Flutter have the same API for Canvas, as they use the same low-level Skia engine .
Not.
Flutter has two classes for drawing on Canvas - CustomPaint and CustomPainter . The second implements your rendering algorithm.
Read more here: StackOverflow
How to create custom widgets?
Compose widgets inside one (instead of inheritance).
In Android, we can inherit from the View we are interested in and add our own logic. In Flutter, this is similar to a ViewGroup, only the widget is always inherited from StatelessWidget or StatefulWidget. Those. you need to create a new widget and use it the set of widgets you need as parameters or fields.
What is the analogue of Intent in Flutter?
He is not there. To navigate between screens, the Navigator and Route classes are used .
To interact with external components (for example, a camera or file picker), you can use plugins or native integration on each platform. Learn more about native integration: Developing Packages and Plugins .
Flutter has no such thing as Activity and Fragment. There are Navigator (Navigator) and Routes (routes). The Flutter application resembles a single-activity application, where different screens represent different fragments, and the FragmentManager controls them. Navigator is similar to the FragmentManager in principle. It can do push () or pop () to the route you specify. Route is a kind of Fragment, but in Flutter it is customary to compare it with a screen or page.
In Android, we describe all Activities between which we can navigate in AndroidManifest.xml.
Flutter has two ways:
How to process intents coming from other applications?
Interacting with the Android layer of the application through the MethodChannel .
We write intent-filter in AndroidManifest.xml:
We process the Intent in MainActivity and call the code from the Flutter via the MethodChannel:
We request data when the widget begins to be drawn:
What is the analogue of startActivityForResult () ?
The await keyword and the result of a Future class .
After calling startActivityForResult () in Android, we need to implement the processing in onActivityResult (). Flutter does not need to implement anything, because navigator method push () returns a Future object.
And when we got the coordinates on the '/ location' screen, do pop ():
What is the analogue of runOnUiThread () in Flutter?
Dart implements a single-threaded execution model that runs on Isolates . Asynchronous execution uses async / await, which you may be familiar with from C #, JavaScript, or Kotlin coroutines.
Fulfilling the request and returning the result for updating the UI:
When the response to the request is received, you need to call the setState () method to redraw the widget tree with the new data.
Loading and updating data in a ListView :
How to execute code in a background thread?
As mentioned above - using async / await and isolation (Isolate).
Out of the box on Android, you can use AsyncTask. You need to implement onPreExecute () , doInBackground () , onPostExecute () in it . In Flutter “out of the box” you just need to use async / await, Dart will take care of the rest.
Here the dataLoader () method is isolated. In isolation, you can run heavy operations such as parsing large JSONs, encryption, image processing, etc.
What is OkHttp 's Flutter equivalent ?
Flutter has its own HTTP package .
So far, not all OkHttp features have been implemented in the HTTP Package, so many of the missing features are abstracted and you can implement them yourself as needed.
To use the HTTP package, add it as a dependency in pubspec.yaml:
To execute the request, call await in the async function http.get ():
How to show progress?
Using the ProgressIndicator widget .
Where to store resources of different resolutions?
In assets.
In Android, resources have a res folder and have assets. Flutter has only assets. The assets folder can be located anywhere in the project, most importantly, write the path to it in the pubspec.yaml file.
Comparison of the sizes of graphic resources in Android and Flutter.
Flutter uses AssetManager or specialized classes starting with Asset to use resources in the code.
AssetManager:
Resource Location:
Path in pubspec.yaml file:
Using AssetImage :
Using asset directly:
Where to store strings? How to localize them?
Store in static fields. Localize using intl package .
What is the analogue of the gradle file? How to add dependencies?
pubspec.yaml.
Flutter delegates the assembly to native Android and iOS builders. See a list of all the popular libraries for Flutter at Pub .
What is the equivalent of Activity and Fragment in Flutter?
Everything in Flutter is widgets. The role of activity and fragments for working with the UI is played by widgets. And the role of navigation, as mentioned in the paragraph on navigation, is Navigator and Route.
Flutter For Android Developers: How to design an Activity UI in Flutter .
How to handle life cycle events?
Using the WidgetsBinding and didChangeAppLifecycleState () method .
Flutter uses FlutterActivity in native code, and the Flutter engine makes processing state changes as inconspicuous as possible. But if you still need to do some work depending on the state, then the life cycle is slightly different:
This is described in more detail in the AppLifecycleStatus documentation .
What is the analog of LinearLayout ?
Row for horizontal, Column for vertical.
Flutter For Android Developers: How to design LinearLayout in Flutter?
What is the equivalent of RelativeLayout ?
Widget Stack .
What is the analogue of ScrollView ?
ListView with widgets.
How to handle transitions between portrait and landscape?
FlutterView handles flips if AndroidManifest.xml contains
android: configChanges = "orientation | screenSize"
How to add onClick listener for widget in Flutter?
If the widget supports clicks, then in onPressed (). If not, then in onTap ().
In onPressed ():
In onTap ():
How to handle other gestures on widgets?
Using the GestureDetector . They can handle the following actions:
Processing onDoubleTap:
What is the analogue of ListView in Flutter?
The ListView .
Flutter doesn't have to think about cleaning up and reusing items (which is what ListView / RecyclerView does in Android, using the ViewHolder pattern).
How to find out which element was clicked on?
Wrapping an item in a GestureDetector .
How to dynamically update a ListView ?
If you have a small data set, then this can be done through setState () . If the data set is large, then through ListView.Builder , which is an analogue of RecyclerView .
Using setState () :
Using ListView.Builder :
How to use custom fonts?
You just need to put the font file in the folder (think of the name yourself) and indicate the path to it in pubspec.yaml.
How to style text widgets?
Using parameters:
More details here: Retrieve the value of a text field .
What is the analogue of hint in TextInput ?
The tooltip can be shown using InputDecoration , passing it as a constructor to the widget.
How to show validation errors?
All the same with InputDecoration and its state.
How to access GPS?
Using the geolocator plugin .
How to access the camera?
Using the image_picker plugin .
How to log in via Facebook?
Using the flutter_facebook_login plugin .
How to use firebase?
Firebase supports Flutter first party plugins .
How to do native (platform) code insertions?
Flutter uses EventBus to interact with platform code. Details here: developing packages and plugins .
How to use NDK?
Write your own plugin for the interaction of your NDK code with Flutter. While Flutter does not support direct interaction.
How to use the theme (Theme) in the application?
Using the MaterialApp widget or WidgetApp as the root in the application.
How to access Shared Preferences?
Using the Shared_Preferences plugin (for NSUserDefaults on iOS too).
How to access SQLite in Flutter?
Using the SQFlite plugin .
How to show push notification?
Using the Firebase_Messaging plugin .
New programming languages and frameworks appear almost constantly. And at the start it is difficult to understand what will shoot and will live for a long time, and what will be forgotten in a year. Bob Martin, in his book The Ideal Programmer, encourages us to learn new programming languages and frameworks. Chad Fowler, in his book The Fanatic Programmer, advises you to always be on the cutting edge of technology. But how to understand that you were not mistaken with the choice? In 2016, I drew attention to Kotlin, but because of the high workload, I could not devote enough time to it until the second half of 2017. At the start, many were skeptical about it, and now it is one of the most popular programming languages, and a huge number of developers create on him his products. I feel that in those one and a half years I could have gained a deeper understanding of the subtleties of the language.
In the same 2016, the Flutter framework in the Dart language appeared. But the growth of his popularity was not so rapid, and only in 2018 they talked about him loudly. Then I also wanted to try it in action. And I liked it! Time will tell what the future holds for this framework, but it seems to be very promising. (And if Google Fuchsia shoots, then, without a doubt, Flutter will not be left behind). To study it or not is up to you! In any case, learning the new is a great workout for the brain. That’s all for me. May Google not break your Play!
If you want to understand what this framework is good for, and to evaluate how much effort you will have to make in order to use it, welcome to cat.
Content:
- Views
- What is the equivalent of View in Flutter?
- How to update the display of widgets?
- How to layout a screen with widgets? Where is the XML layout file?
- How to add or remove a component in layout while the application is running?
- How to animate widgets?
- How to use Canvas?
- How to create custom widgets?
- Intents
- What is the analogue of Intent in Flutter?
- How to process intents coming from other applications?
- What is the analogue of startActivityForResult ()?
- Async ui
- What is the analogue of runOnUiThread () in Flutter?
- How to execute code in a background thread?
- What is OkHttp's Flutter equivalent?
- How to show progress?
- Project Structure and Resources
- Where to store resources of different resolutions?
- Where to store strings? How to localize them?
- What is the analogue of the gradle file? How to add dependencies?
- Activities & Fragments
- Layouts
- What is the analog of LinearLayout?
- What is the equivalent of RelativeLayout?
- What is the analogue of ScrollView?
- How to handle transitions between portrait and landscape?
- Gestures and touch event handling.
- Listviews & adapters
- What is the analogue of ListView in Flutter?
- How to find out which element was clicked on?
- How to dynamically update a ListView?
- Work with text
- Input form
- Flutter Plugins
- How to access GPS?
- How to access the camera?
- How to log in via Facebook?
- How to use firebase?
- How to do native (platform) code insertions?
- How to use NDK?
- Themes
- Databases and local storage
- Notifications
Views
Question:
What is the equivalent of View in Flutter?
Answer:
Widget
Differences:
View - in fact, what will be on the screen. Invalidate () is called to display the changes.
Widget - a description of what will be on the screen. For change is created anew.
Additional Information:
When launched on Android itself, View is under the hood of Widget. Flutter includes the Material Components library . It contains widgets that implement Material Design guidelines .
Question:
How to update the display of widgets?
Answer:
Using StatefulWidget and its State . Flutter has 2 kinds of widgets: StatelessWidget and StatefulWidget . They work the same way, the only difference is in the rendering state.
Differences:
StatelessWidget has an immutable state. Suitable for displaying text, logo, etc. Those. if the element on the screen should not change during the entire display time, then it suits you. It can also be used as a container for stateful widgets.
StatefulWidget has the State state, which stores information about the current state. If you want to change an element on the screen when performing some action (a response came from the server, the user clicked on a button, etc.) - this is your option.
Example:
1) StatelessWidget - Text
Text(
'I like Flutter!',
style: TextStyle(fontWeight: FontWeight.bold),
);
2) StatefulWidget - when you click on the (FloatingActionButton) button, the text in the Text widget changes from “I Like Flutter” to “Flutter is Awesome!”.
import 'package:flutter/material.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// Этот виджет корневой в приложении.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State {
// дефолтный текст
String textToShow = "Мне нравится Flutter";
void _updateText() {
setState(() {
// обновление текста
textToShow = "Flutter крутой!";
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: Center(child: Text(textToShow)),
floatingActionButton: FloatingActionButton(
onPressed: _updateText,
tooltip: 'Обновить текст',
child: Icon(Icons.update),
),
);
}
}
Question:
How to layout a screen with widgets? Where is the XML layout file?
Answer:
Flutter has no XML layout for screens. Everything typeset in the widget tree directly in the code.
Example:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: Center(
child: MaterialButton(
onPressed: () {},
child: Text('Hello'),
padding: EdgeInsets.only(left: 10.0, right: 10.0),
),
),
);
}
All default widgets in Flutter can be viewed in the widget catalog .
Question:
How to add or remove a component in layout while the application is running?
Answer:
Through a function that will return the desired widget depending on the state.
Differences:
On Android, you can do addView () or removeView () in a ViewGroup. In Flutter it’s not possible, because widgets are unchanged. Only their condition can change.
Example:
How to change Text to Button by clicking on FloatingActionButton.
import 'package:flutter/material.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// Этот виджет корневой в приложении.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State {
// Дефолтное значение для флага
bool toggle = true;
void _toggle() {
setState(() {
toggle = !toggle;
});
}
_getToggleChild() {
if (toggle) {
return Text('Toggle One');
} else {
return MaterialButton(onPressed: () {}, child: Text('Toggle Two'));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: Center(
child: _getToggleChild(),
),
floatingActionButton: FloatingActionButton(
onPressed: _toggle,
tooltip: 'Update Text',
child: Icon(Icons.update),
),
);
}
}
Question:
How to animate widgets?
Answer:
Using the AnimationController class , which is the descendant of the Animation abstract class
Differences:
On Android, you can create animations in XML or animate View using animate (). In Flutter, animation must be written in code using the AnimationController.
Additional Information:
You can learn more in the Animation & Motion widgets , Animations tutorial, and Animations overview .
Example:
Fade animation of the Flutter logo.
import 'package:flutter/material.dart';
void main() {
runApp(FadeAppTest());
}
class FadeAppTest extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fade Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyFadeTest(title: 'Fade Demo'),
);
}
}
class MyFadeTest extends StatefulWidget {
MyFadeTest({Key key, this.title}) : super(key: key);
final String title;
@override
_MyFadeTest createState() => _MyFadeTest();
}
class _MyFadeTest extends State with TickerProviderStateMixin {
AnimationController controller;
CurvedAnimation curve;
@override
void initState() {
super.initState();
controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this);
curve = CurvedAnimation(parent: controller, curve: Curves.easeIn);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Container(
child: FadeTransition(
opacity: curve,
child: FlutterLogo(
size: 100.0,
)))),
floatingActionButton: FloatingActionButton(
tooltip: 'Fade',
child: Icon(Icons.brush),
onPressed: () {
controller.forward();
},
),
);
}
}
Question:
How to use Canvas ?
Answer:
Android and Flutter have the same API for Canvas, as they use the same low-level Skia engine .
Differences:
Not.
Additional Information:
Flutter has two classes for drawing on Canvas - CustomPaint and CustomPainter . The second implements your rendering algorithm.
Read more here: StackOverflow
Example:
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: DemoApp()));
class DemoApp extends StatelessWidget {
Widget build(BuildContext context) => Scaffold(body: Signature());
}
class Signature extends StatefulWidget {
SignatureState createState() => SignatureState();
}
class SignatureState extends State {
List _points = [];
Widget build(BuildContext context) {
return GestureDetector(
onPanUpdate: (DragUpdateDetails details) {
setState(() {
RenderBox referenceBox = context.findRenderObject();
Offset localPosition =
referenceBox.globalToLocal(details.globalPosition);
_points = List.from(_points)..add(localPosition);
});
},
onPanEnd: (DragEndDetails details) => _points.add(null),
child: CustomPaint(painter: SignaturePainter(_points), size: Size.infinite),
);
}
}
class SignaturePainter extends CustomPainter {
SignaturePainter(this.points);
final List points;
void paint(Canvas canvas, Size size) {
var paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0;
for (int i = 0; i < points.length - 1; i++) {
if (points[i] != null && points[i + 1] != null)
canvas.drawLine(points[i], points[i + 1], paint);
}
}
bool shouldRepaint(SignaturePainter other) => other.points != points;
}
Question:
How to create custom widgets?
Answer:
Compose widgets inside one (instead of inheritance).
Differences:
In Android, we can inherit from the View we are interested in and add our own logic. In Flutter, this is similar to a ViewGroup, only the widget is always inherited from StatelessWidget or StatefulWidget. Those. you need to create a new widget and use it the set of widgets you need as parameters or fields.
Example:
class CustomButton extends StatelessWidget {
final String label;
CustomButton(this.label);
@override
Widget build(BuildContext context) {
return RaisedButton(onPressed: () {}, child: Text(label));
}
}
@override
Widget build(BuildContext context) {
return Center(
child: CustomButton("Hello"),
);
}
Intents
Question:
What is the analogue of Intent in Flutter?
Answer:
He is not there. To navigate between screens, the Navigator and Route classes are used .
To interact with external components (for example, a camera or file picker), you can use plugins or native integration on each platform. Learn more about native integration: Developing Packages and Plugins .
Differences:
Flutter has no such thing as Activity and Fragment. There are Navigator (Navigator) and Routes (routes). The Flutter application resembles a single-activity application, where different screens represent different fragments, and the FragmentManager controls them. Navigator is similar to the FragmentManager in principle. It can do push () or pop () to the route you specify. Route is a kind of Fragment, but in Flutter it is customary to compare it with a screen or page.
In Android, we describe all Activities between which we can navigate in AndroidManifest.xml.
Flutter has two ways:
- Describe a Map named Route (MaterialApp)
- Navigate directly to Route (WidgetApp).
Example:
void main() {
runApp(MaterialApp(
home: MyAppHome(), // becomes the route named '/'
routes: {
'/a': (BuildContext context) => MyPage(title: 'page A'),
'/b': (BuildContext context) => MyPage(title: 'page B'),
'/c': (BuildContext context) => MyPage(title: 'page C'),
},
));
}
Navigator.of(context).pushNamed('/b');
Question:
How to process intents coming from other applications?
Answer:
Interacting with the Android layer of the application through the MethodChannel .
Example:
We write intent-filter in AndroidManifest.xml:
We process the Intent in MainActivity and call the code from the Flutter via the MethodChannel:
package com.example.shared;
import android.content.Intent;
import android.os.Bundle;
import java.nio.ByteBuffer;
import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.ActivityLifecycleListener;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
private String sharedText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();
if (Intent.ACTION_SEND.equals(action) && type != null) {
if ("text/plain".equals(type)) {
handleSendText(intent); // Handle text being sent
}
}
new MethodChannel(getFlutterView(), "app.channel.shared.data").setMethodCallHandler(
new MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.contentEquals("getSharedText")) {
result.success(sharedText);
sharedText = null;
}
}
});
}
void handleSendText(Intent intent) {
sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
}
}
We request data when the widget begins to be drawn:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample Shared App Handler',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State {
static const platform = const MethodChannel('app.channel.shared.data');
String dataShared = "No data";
@override
void initState() {
super.initState();
getSharedText();
}
@override
Widget build(BuildContext context) {
return Scaffold(body: Center(child: Text(dataShared)));
}
getSharedText() async {
var sharedData = await platform.invokeMethod("getSharedText");
if (sharedData != null) {
setState(() {
dataShared = sharedData;
});
}
}
}
Question:
What is the analogue of startActivityForResult () ?
Answer:
The await keyword and the result of a Future class .
Differences:
After calling startActivityForResult () in Android, we need to implement the processing in onActivityResult (). Flutter does not need to implement anything, because navigator method push () returns a Future object.
Example:
Map coordinates = await Navigator.of(context).pushNamed('/location');
And when we got the coordinates on the '/ location' screen, do pop ():
Navigator.of(context).pop({"lat":43.821757,"long":-79.226392});
Async ui
Question:
What is the analogue of runOnUiThread () in Flutter?
Answer:
Dart implements a single-threaded execution model that runs on Isolates . Asynchronous execution uses async / await, which you may be familiar with from C #, JavaScript, or Kotlin coroutines.
Example:
Fulfilling the request and returning the result for updating the UI:
loadData() async {
String dataURL = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(dataURL);
setState(() {
widgets = json.decode(response.body);
});
}
When the response to the request is received, you need to call the setState () method to redraw the widget tree with the new data.
Example:
Loading and updating data in a ListView :
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State {
List widgets = [];
@override
void initState() {
super.initState();
loadData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: ListView.builder(
itemCount: widgets.length,
itemBuilder: (BuildContext context, int position) {
return getRow(position);
}));
}
Widget getRow(int i) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Text("Row ${widgets[i]["title"]}")
);
}
loadData() async {
String dataURL = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(dataURL);
setState(() {
widgets = json.decode(response.body);
});
}
}
Question:
How to execute code in a background thread?
Answer:
As mentioned above - using async / await and isolation (Isolate).
Differences:
Out of the box on Android, you can use AsyncTask. You need to implement onPreExecute () , doInBackground () , onPostExecute () in it . In Flutter “out of the box” you just need to use async / await, Dart will take care of the rest.
Example:
Here the dataLoader () method is isolated. In isolation, you can run heavy operations such as parsing large JSONs, encryption, image processing, etc.
loadData() async {
ReceivePort receivePort = ReceivePort();
await Isolate.spawn(dataLoader, receivePort.sendPort);
// The 'echo' isolate sends its SendPort as the first message
SendPort sendPort = await receivePort.first;
List msg = await sendReceive(sendPort, "https://jsonplaceholder.typicode.com/posts");
setState(() {
widgets = msg;
});
}
// The entry point for the isolate
static dataLoader(SendPort sendPort) async {
// Open the ReceivePort for incoming messages.
ReceivePort port = ReceivePort();
// Notify any other isolates what port this isolate listens to.
sendPort.send(port.sendPort);
await for (var msg in port) {
String data = msg[0];
SendPort replyTo = msg[1];
String dataURL = data;
http.Response response = await http.get(dataURL);
// Lots of JSON to parse
replyTo.send(json.decode(response.body));
}
}
Future sendReceive(SendPort port, msg) {
ReceivePort response = ReceivePort();
port.send([msg, response.sendPort]);
return response.first;
}
Полноценный запускаемый пример:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:async';
import 'dart:isolate';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State {
List widgets = [];
@override
void initState() {
super.initState();
loadData();
}
showLoadingDialog() {
if (widgets.length == 0) {
return true;
}
return false;
}
getBody() {
if (showLoadingDialog()) {
return getProgressDialog();
} else {
return getListView();
}
}
getProgressDialog() {
return Center(child: CircularProgressIndicator());
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: getBody());
}
ListView getListView() => ListView.builder(
itemCount: widgets.length,
itemBuilder: (BuildContext context, int position) {
return getRow(position);
});
Widget getRow(int i) {
return Padding(padding: EdgeInsets.all(10.0), child: Text("Row ${widgets[i]["title"]}"));
}
loadData() async {
ReceivePort receivePort = ReceivePort();
await Isolate.spawn(dataLoader, receivePort.sendPort);
// The 'echo' isolate sends its SendPort as the first message
SendPort sendPort = await receivePort.first;
List msg = await sendReceive(sendPort, "https://jsonplaceholder.typicode.com/posts");
setState(() {
widgets = msg;
});
}
// the entry point for the isolate
static dataLoader(SendPort sendPort) async {
// Open the ReceivePort for incoming messages.
ReceivePort port = ReceivePort();
// Notify any other isolates what port this isolate listens to.
sendPort.send(port.sendPort);
await for (var msg in port) {
String data = msg[0];
SendPort replyTo = msg[1];
String dataURL = data;
http.Response response = await http.get(dataURL);
// Lots of JSON to parse
replyTo.send(json.decode(response.body));
}
}
Future sendReceive(SendPort port, msg) {
ReceivePort response = ReceivePort();
port.send([msg, response.sendPort]);
return response.first;
}
}
Question:
What is OkHttp 's Flutter equivalent ?
Answer:
Flutter has its own HTTP package .
Additional Information:
So far, not all OkHttp features have been implemented in the HTTP Package, so many of the missing features are abstracted and you can implement them yourself as needed.
Example:
To use the HTTP package, add it as a dependency in pubspec.yaml:
dependencies:
...
http: ^0.11.3+16
To execute the request, call await in the async function http.get ():
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
[...]
loadData() async {
String dataURL = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(dataURL);
setState(() {
widgets = json.decode(response.body);
});
}
}
Question:
How to show progress?
Answer:
Using the ProgressIndicator widget .
Example:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State {
List widgets = [];
@override
void initState() {
super.initState();
loadData();
}
showLoadingDialog() {
return widgets.length == 0;
}
getBody() {
if (showLoadingDialog()) {
return getProgressDialog();
} else {
return getListView();
}
}
getProgressDialog() {
return Center(child: CircularProgressIndicator());
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: getBody());
}
ListView getListView() => ListView.builder(
itemCount: widgets.length,
itemBuilder: (BuildContext context, int position) {
return getRow(position);
});
Widget getRow(int i) {
return Padding(padding: EdgeInsets.all(10.0), child: Text("Row ${widgets[i]["title"]}"));
}
loadData() async {
String dataURL = "https://jsonplaceholder.typicode.com/posts";
http.Response response = await http.get(dataURL);
setState(() {
widgets = json.decode(response.body);
});
}
}
Project Structure and Resources
Question:
Where to store resources of different resolutions?
Answer:
In assets.
Differences:
In Android, resources have a res folder and have assets. Flutter has only assets. The assets folder can be located anywhere in the project, most importantly, write the path to it in the pubspec.yaml file.
Additional Information:
Comparison of the sizes of graphic resources in Android and Flutter.
Android density qualifier | Flutter pixel ratio |
ldpi | 0.75x |
mdpi | 1.0x |
hdpi | 1.5x |
xhdpi | 2.0x |
xxhdpi | 3.0x |
xxxhdpi | 4.0x |
Example:
AssetManager:
val flutterAssetStream = assetManager.open("flutter_assets/assets/my_flutter_asset.png")
Resource Location:
images/my_icon.png // Base: 1.0x image
images/2.0x/my_icon.png // 2.0x image
images/3.0x/my_icon.png // 3.0x image
Path in pubspec.yaml file:
assets:
- images/my_icon.jpeg
Using AssetImage :
return AssetImage("images/a_dot_burr.jpeg");
Using asset directly:
@override
Widget build(BuildContext context) {
return Image.asset("images/my_image.png");
}
Question:
Where to store strings? How to localize them?
Answer:
Store in static fields. Localize using intl package .
Example:
class Strings {
static String welcomeMessage = "Welcome To Flutter";
}
Text(Strings.welcomeMessage)
Question:
What is the analogue of the gradle file? How to add dependencies?
Answer:
pubspec.yaml.
Additional Information:
Flutter delegates the assembly to native Android and iOS builders. See a list of all the popular libraries for Flutter at Pub .
Activities & Fragments
Question:
What is the equivalent of Activity and Fragment in Flutter?
Answer:
Everything in Flutter is widgets. The role of activity and fragments for working with the UI is played by widgets. And the role of navigation, as mentioned in the paragraph on navigation, is Navigator and Route.
Additional Information:
Flutter For Android Developers: How to design an Activity UI in Flutter .
Question:
How to handle life cycle events?
Answer:
Using the WidgetsBinding and didChangeAppLifecycleState () method .
Additional Information:
Flutter uses FlutterActivity in native code, and the Flutter engine makes processing state changes as inconspicuous as possible. But if you still need to do some work depending on the state, then the life cycle is slightly different:
- inactive - this method is only in iOS, in Android there is no analogue;
- paused - similar to onPause () in Android;
- resumed - similar to onPostResume () in Android;
- suspending - similar to onStop in Android, in iOS there is no analogue.
This is described in more detail in the AppLifecycleStatus documentation .
Example:
import 'package:flutter/widgets.dart';
class LifecycleWatcher extends StatefulWidget {
@override
_LifecycleWatcherState createState() => _LifecycleWatcherState();
}
class _LifecycleWatcherState extends State with WidgetsBindingObserver {
AppLifecycleState _lastLifecycleState;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() {
_lastLifecycleState = state;
});
}
@override
Widget build(BuildContext context) {
if (_lastLifecycleState == null)
return Text('This widget has not observed any lifecycle changes.', textDirection: TextDirection.ltr);
return Text('The most recent lifecycle state this widget observed was: $_lastLifecycleState.',
textDirection: TextDirection.ltr);
}
}
void main() {
runApp(Center(child: LifecycleWatcher()));
}
Layouts
Question:
What is the analog of LinearLayout ?
Answer:
Row for horizontal, Column for vertical.
Additional Information:
Flutter For Android Developers: How to design LinearLayout in Flutter?
Example:
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Row One'),
Text('Row Two'),
Text('Row Three'),
Text('Row Four'),
],
);
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Column One'),
Text('Column Two'),
Text('Column Three'),
Text('Column Four'),
],
);
}
Question:
What is the equivalent of RelativeLayout ?
Answer:
Widget Stack .
More details:
StackoverflowQuestion:
What is the analogue of ScrollView ?
Answer:
ListView with widgets.
Example:
@override
Widget build(BuildContext context) {
return ListView(
children: [
Text('Row One'),
Text('Row Two'),
Text('Row Three'),
Text('Row Four'),
],
);
}
Question:
How to handle transitions between portrait and landscape?
Answer:
FlutterView handles flips if AndroidManifest.xml contains
android: configChanges = "orientation | screenSize"
Gestures and touch event handling
Question:
How to add onClick listener for widget in Flutter?
Answer:
If the widget supports clicks, then in onPressed (). If not, then in onTap ().
Example:
In onPressed ():
@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () {
print("click");
},
child: Text("Button"));
}
In onTap ():
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
child: FlutterLogo(
size: 200.0,
),
onTap: () {
print("tap");
},
),
));
}
}
Question:
How to handle other gestures on widgets?
Answer:
Using the GestureDetector . They can handle the following actions:
Tap
Double tap
Long press
Vertical drag
Horizontal drag
Example:
Processing onDoubleTap:
AnimationController controller;
CurvedAnimation curve;
@override
void initState() {
controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this);
curve = CurvedAnimation(parent: controller, curve: Curves.easeIn);
}
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
child: RotationTransition(
turns: curve,
child: FlutterLogo(
size: 200.0,
)),
onDoubleTap: () {
if (controller.isCompleted) {
controller.reverse();
} else {
controller.forward();
}
},
),
));
}
}
Listviews & adapters
Question:
What is the analogue of ListView in Flutter?
Answer:
The ListView .
Differences:
Flutter doesn't have to think about cleaning up and reusing items (which is what ListView / RecyclerView does in Android, using the ViewHolder pattern).
Example:
import 'package:flutter/material.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: ListView(children: _getListData()),
);
}
_getListData() {
List widgets = [];
for (int i = 0; i < 100; i++) {
widgets.add(Padding(padding: EdgeInsets.all(10.0), child: Text("Row $i")));
}
return widgets;
}
}
Question:
How to find out which element was clicked on?
Answer:
Wrapping an item in a GestureDetector .
Example:
import 'package:flutter/material.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: ListView(children: _getListData()),
);
}
_getListData() {
List widgets = [];
for (int i = 0; i < 100; i++) {
widgets.add(GestureDetector(
child: Padding(
padding: EdgeInsets.all(10.0),
child: Text("Row $i")),
onTap: () {
print('row tapped');
},
));
}
return widgets;
}
}
Question:
How to dynamically update a ListView ?
Answer:
If you have a small data set, then this can be done through setState () . If the data set is large, then through ListView.Builder , which is an analogue of RecyclerView .
Example:
Using setState () :
import 'package:flutter/material.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State {
List widgets = [];
@override
void initState() {
super.initState();
for (int i = 0; i < 100; i++) {
widgets.add(getRow(i));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: ListView(children: widgets),
);
}
Widget getRow(int i) {
return GestureDetector(
child: Padding(
padding: EdgeInsets.all(10.0),
child: Text("Row $i")),
onTap: () {
setState(() {
widgets = List.from(widgets);
widgets.add(getRow(widgets.length + 1));
print('row $i');
});
},
);
}
}
Using ListView.Builder :
import 'package:flutter/material.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State {
List widgets = [];
@override
void initState() {
super.initState();
for (int i = 0; i < 100; i++) {
widgets.add(getRow(i));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: ListView.builder(
itemCount: widgets.length,
itemBuilder: (BuildContext context, int position) {
return getRow(position);
}));
}
Widget getRow(int i) {
return GestureDetector(
child: Padding(
padding: EdgeInsets.all(10.0),
child: Text("Row $i")),
onTap: () {
setState(() {
widgets.add(getRow(widgets.length + 1));
print('row $i');
});
},
);
}
}
Work with text
Question:
How to use custom fonts?
Answer:
You just need to put the font file in the folder (think of the name yourself) and indicate the path to it in pubspec.yaml.
Example:
fonts:
- family: MyCustomFont
fonts:
- asset: fonts/MyCustomFont.ttf
- style: italic
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: Center(
child: Text(
'This is a custom font text',
style: TextStyle(fontFamily: 'MyCustomFont'),
),
),
);
}
Question:
How to style text widgets?
Answer:
Using parameters:
- color;
- decoration;
- decorationColor;
- decorationStyle;
- fontFamily;
- fontSize;
- fontStyle;
- fontWeight;
- hashCode;
- height;
- inherit;
- letterSpacing;
- textBaseline;
- wordSpacing.
Input form
More details here: Retrieve the value of a text field .
Question:
What is the analogue of hint in TextInput ?
Answer:
The tooltip can be shown using InputDecoration , passing it as a constructor to the widget.
Example:
body: Center(
child: TextField(
decoration: InputDecoration(hintText: "This is a hint"),
)
)
Question:
How to show validation errors?
Answer:
All the same with InputDecoration and its state.
Example:
import 'package:flutter/material.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State {
String _errorText;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: Center(
child: TextField(
onSubmitted: (String text) {
setState(() {
if (!isEmail(text)) {
_errorText = 'Error: This is not an email';
} else {
_errorText = null;
}
});
},
decoration: InputDecoration(hintText: "This is a hint", errorText: _getErrorText()),
),
),
);
}
_getErrorText() {
return _errorText;
}
bool isEmail(String em) {
String emailRegexp =
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regExp = RegExp(emailRegexp);
return regExp.hasMatch(em);
}
}
Flutter Plugins
Question:
How to access GPS?
Answer:
Using the geolocator plugin .
Question:
How to access the camera?
Answer:
Using the image_picker plugin .
Question:
How to log in via Facebook?
Answer:
Using the flutter_facebook_login plugin .
Question:
How to use firebase?
Answer:
Firebase supports Flutter first party plugins .
- firebase_admob for Firebase AdMob.
- firebase_analytics for Firebase Analytics.
- firebase_auth for Firebase Auth.
- firebase_database for Firebase RTDB.
- firebase_storage for Firebase Cloud Storage.
- firebase_messaging for Firebase Messaging (FCM).
- flutter_firebase_ui for quick integration of Firebase Auth (Facebook, Google, Twitter and e-mail).
- cloud_firestore for Firebase Cloud Firestore.
Question:
How to do native (platform) code insertions?
Answer:
Flutter uses EventBus to interact with platform code. Details here: developing packages and plugins .
Question:
How to use NDK?
Answer:
Write your own plugin for the interaction of your NDK code with Flutter. While Flutter does not support direct interaction.
Themes
Question:
How to use the theme (Theme) in the application?
Answer:
Using the MaterialApp widget or WidgetApp as the root in the application.
Example:
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
textSelectionColor: Colors.red
),
home: SampleAppPage(),
);
}
}
Databases and local storage
Question:
How to access Shared Preferences?
Answer:
Using the Shared_Preferences plugin (for NSUserDefaults on iOS too).
Example:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: Center(
child: RaisedButton(
onPressed: _incrementCounter,
child: Text('Increment Counter'),
),
),
),
),
);
}
_incrementCounter() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int counter = (prefs.getInt('counter') ?? 0) + 1;
print('Pressed $counter times.');
prefs.setInt('counter', counter);
}
Question:
How to access SQLite in Flutter?
Answer:
Using the SQFlite plugin .
Notifications
Question:
How to show push notification?
Answer:
Using the Firebase_Messaging plugin .
Conclusion
New programming languages and frameworks appear almost constantly. And at the start it is difficult to understand what will shoot and will live for a long time, and what will be forgotten in a year. Bob Martin, in his book The Ideal Programmer, encourages us to learn new programming languages and frameworks. Chad Fowler, in his book The Fanatic Programmer, advises you to always be on the cutting edge of technology. But how to understand that you were not mistaken with the choice? In 2016, I drew attention to Kotlin, but because of the high workload, I could not devote enough time to it until the second half of 2017. At the start, many were skeptical about it, and now it is one of the most popular programming languages, and a huge number of developers create on him his products. I feel that in those one and a half years I could have gained a deeper understanding of the subtleties of the language.
In the same 2016, the Flutter framework in the Dart language appeared. But the growth of his popularity was not so rapid, and only in 2018 they talked about him loudly. Then I also wanted to try it in action. And I liked it! Time will tell what the future holds for this framework, but it seems to be very promising. (And if Google Fuchsia shoots, then, without a doubt, Flutter will not be left behind). To study it or not is up to you! In any case, learning the new is a great workout for the brain. That’s all for me. May Google not break your Play!