[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.



    Content:


    1. Views

    2. Intents

    3. Async ui

    4. Project Structure and Resources

    5. Activities & Fragments

    6. Layouts

    7. Gestures and touch event handling.

    8. Listviews & adapters

    9. Work with text

    10. Input form

    11. Flutter Plugins

    12. Themes

    13. Databases and local storage

    14. 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. 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.

    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 qualifierFlutter pixel ratio
    ldpi0.75x
    mdpi1.0x
    hdpi1.5x
    xhdpi2.0x
    xxhdpi3.0x
    xxxhdpi4.0x
    Flutter uses AssetManager or specialized classes starting with Asset to use resources in the code.

    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:

    Stackoverflow

    Question:


    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 .


    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!

    Also popular now: