Flutter Dev

Flutter Dev

1560831976

Let’s Develop a Mobile App in Flutter

Flutter is a mobile framework for the development of multiplatform native apps, so from one code we will get an app that can run on Android as well as on iOS. It is a similar experiment to Xamarin from Microsoft a few years ago.

Let’s Develop a Mobile App in Flutter (1/3)

Flutter developers released a second beta version of the Flutter framework at the beginning of April. It is the second beta version released in just the last month and a half. It seems that after a long development period, there will be a stable version of the framework released quite soon.

Flutter developers released a second beta version of the Flutter framework at the beginning of April. It is the second beta version released in just the last month and a half. It seems that after a long development period, there will be a stable version of the framework released quite soon. In this article we will present the basic interface setting for using Flutter and describe its project structure. We will also use a simple project from the Flutter developers to show how to create an app for Android and iOS that has a UI corresponding to the given platform.

If you are a Flutter guru already (or if you know your way around it), you will not find much new information in this article. But we believe that you will discover some interesting tips and tricks in this series nonetheless.

Let’s get started

The Dart programming language is used for creating Flutter applications. Dart is an object oriented programming language and its syntax is very similar to Kotlin’s, even though it might be a bit more wordy. You can check its specifications and syntax on the official website. We will not discuss Dart much in this article but we will still present a few basic language constructs.

SDKs

We need to have the Flutter and Dart SDKs installed in order to be able to start using Flutter. The installation of the SDK packages is partially dependent on your operating system. We will show only the main commands here, other details and differences can be found on the official Flutter webpage. Because Flutter is in beta, the SDK installation is performed by cloning the official repository, therefore you need to have a git client installed.

git clone -b beta https://github.com/flutter/flutter.git

Then you need to add the path to Flutter to the interface variable PATH. After a correct setup we can call Flutter relatively, thanks to which we ensure the correct function of all other available Flutter tools. Next, we will need the SDK and the relevant development tools for our intended platform. The only development restriction for iOS (or let’s say of the final app assembly) is the necessity of having a device with macOS. Finally, we need to install all the dependent components using the following command. The same command can also be used for the overall configuration check of our interface.

flutter doctor

As Flutter is constantly being developed, the stable SDK version changes on a daily basis. That is why we recommend running the following command every time before you start or continue the development of your Flutter app. It will perform the update of all necessary SDK packages.

flutter upgrade

Editor

We recommend using Android Studio for the development. Flutter developers offer a plugin that will make the creation of your new project a piece of cake. All the following examples will be presented in Android Studio. You can find all the necessary details for the installation of Android Studio and the plugin on the official Flutter website.

Project creation

If you have the Android Studio and the plugin installed, the creation of a new project is a cakewalk. Select the Create New Flutter Project option and go through a simple wizard that will help you set up the basic project configuration.

After the creation, the project contains all the necessary parts for a correct setup and run of the final app as well as a demo project prepared by Flutter developers. In the next part we will use this demo project to show how to create apps for both platforms with ease. In the end, the apps will look as if they are created by a native development process.

Project’s structure

The structure of every Flutter project looks more or less like this:

  • The android directory – here we will find the classic Android project. In the app module we can find the actual Android app that already contains a few generated files. These are for connecting the Flutter app to the classic Android app. All Android specific configurations, the applicationId definition for example, supported SDKs, or signing certificates will be modified here.
  • The ios directory – here we will find the typical iOS project. It already contains a few generated files used for connecting the Flutter app to the classic iOS app. All iOS specific configuration is to be done in this directory
  • The lib directory – the most important directory where the Flutter app files are stored. The main file here is main.dart containing the void main() function which is the input function for the Flutter app.
  • File pubspec.yaml – this file is for the Flutter app configuration. We also define the dependencies on external Flutter modules and Dart libraries in this file. For example, to add a library for detecting information about the device on which the app runs, we only need to add the following dependency.
dependencies:
  ...
  device_info: "^0.2.0"

Flutter application

Assembly of the app

To assemble the final app, we only need to enter one of the following commands, depending on the selected platform. The assembly can be parameterized to a certain degree. The currently supported parameters can be displayed by the last two commands.

flutter build apk          # Build an Android APK file from your app.
flutter build ios          # Build an iOS application bundle (Mac OS X host only).
flutter build apk --help   # Print build parameters
flutter build ios --help   # Print build parameters

As you can see, the final apps created from one code for iOS and Android look alike. After installing the demo app on a specific end device (iPhone X, Pixel 2 XL), it looks like this:

Expanding the demo app

Let’s add more simple logic to the app. After every fifth click on the + button, the app will show a simple dialogue with a query if the user wishes to reset the counter or continue. The user will then, using the dialogue buttons, choose one option. When the OK button is pressed, the counter will reset and the dialogue ends. When pressing the Cancel button, the dialogue will end. At the end of the _incrementCounter method from the _MyHomePageState class, we will add the following code:

if (_counter % 5 == 0) {
  showDialog(
      context: context,
      barrierDismissible: false,
      builder: (BuildContext context) {
        return /*TODO create dialog*/
      });
}

Every time the variable _counter is divisible by five, the dialogue will be shown, so everything is set up as we wanted. Now we need to create a function that will create a dialogue with the requested functionality.

Material design dialogue

Let’s add a new method that will create a new alert dialogue in material design to the _MyHomePageState class. The basis is the AlertDialog class, which awaits in the constructor for the configuration of the dialogue being created. An important parameter is the actions field where the individual dialogue buttons are defined. The method for creating the dialogue is as follows:

AlertDialog _createMaterialAlertDialog() => new AlertDialog(
    title: new Text('Reset counter'),
    content: new Text('Do you want to reset counter?'),
    actions: <Widget>[
      new MaterialButton(
        onPressed: () {
          Navigator.pop(context);
        },
        child: new Text('Cancel'),
      ),
      new MaterialButton(
          onPressed: () {
            setState(() {
              _counter = 0;
            });
            Navigator.pop(context);
          },
          child: new Text('OK')),
    ],
  );

iOS design dialogue

The dialogue creation is the same as the preceding one, but instead of the material design component we will use components from the cupertino package. The method for creating the dialogue is as follows:

CupertinoAlertDialog _createCupertinoAlertDialog() => new CupertinoAlertDialog(
    title: new Text('Reset counter'),
    content: new Text('Do you want to reset counter?'),
    actions: <Widget>[
      new CupertinoButton(
        onPressed: () {
          Navigator.pop(context);
        },
        child: new Text('Cancel'),
      ),
      new CupertinoButton(
          onPressed: () {
            setState(() {
              _counter = 0;
            });
            Navigator.pop(context);
          },
          child: new Text('OK')),
    ],
  );

Dialogue based on the target platform

Let’s now combine both concepts together and add a logic that will ensure that the correct dialogue is displayed based on the platform on which the app is running. It is quite simple; we only need to ask what the used platform is.

if (_counter % 5 == 0) {
  showDialog(
      context: context,
      barrierDismissible: false,
      builder: (BuildContext context) {
        return Platform.isIOS
            ? _createCupertinoAlertDialog()
            : _createMaterialAlertDialog();
      });
}

As you have probably noticed, the app now contains a lot of redundant code – the source code for both methods for the creation of corresponding dialogues is virtually the same. It differs only in places where we are calling the appropriate class for displaying a specific element. In one of the following articles we will show how to, at least partially, get rid of these redundancies.

Conclusion

In today’s article we did an overview of Flutter, showed the initial steps needed before starting the creation of a Flutter app and described the parts of the Flutter project. We also extended the prepared demo project with the platform dependent dialogue displaying functionality. You can find the whole project at our GitHub.

We will present the basic constructions of the Dart programming language next. We will also show how Flutter actually works from the inside and start creating an app for displaying events, event registrations, and notifications about any event changes.

Let’s Develop a Mobile App in Flutter (2/3)

Language Dart you need to know if you want a Flutter flow. That’s why we’ll have a look at how to write and design in Flutter. We’ll answer the basic question: Why does it work cross-platform? And we’ll also call a native service for the given platform.

The last article presented the Flutter framework: We’ve set up the environment in order to be able to start developing the Flutter application and described the structure of the Flutter app project. We also presented how to create the platform-dependent dialogue.

Today, we’ll introduce Flutter in more technical details. We’ll go over the Dart language in order to understand how to actually write in Flutter. We’ll talk about Flutter from a design perspective and explain why it works cross-platform. And we’ll show how to call a native service for the given platform. Let’s get started.

Language class

Dart is an object-oriented programming language with syntax very similar to today’s modern languages such as Kotlin, C# or Java.

Variables

Dart is a strongly typed language. That means that every variable has its own data type that is determined when the variable is created and it cannot be changed when the program is running. You don’t have to explicitly state the data type when creating a new variable. You can use the keyword var to deduce its type. Even though it’s a strongly typed language, you can create a variable “without any data type” by using the keyword dynamic.

// explicit data type
String firstName = 'Eman';

// data type deduction
var surname = 'Novák';

// variable data type
dynamic company = 'eMan.cz';

//firstName = 10; // compile error
//surname = 10; // compile error
company = 10;

Classes and objects

Everything is an object in Dart. Every object is an instance of a class and all classes inherit from the Object class. The classes consist of class variables and methods (static methods); objects consist of instance variables and methods.

One of the concepts of object-oriented programming is the encapsulation of classes. In Java, for example, you can define several visibility levels, usually by using keywords such as private, protected, or public. Only the private and public visibility, that which is not defined by special keywords, exist in Dart. But while declaring a class, variable etc. for private visibility, it’s sufficient to use the _ prefix in the name of the element. The visibility does not apply to the classes’ level but to the level of the individual app parts – libraries (library).

class _MyPrivateClass {
  String _myPrivateVariable = '...' // private variable in private class
  String myPublicVariable = '...' // public variable in private class
}

class MyPublicClass {
  String _myPrivateVariable = '...' // private variable in public class
  String myPublicVariable = '...' // public variable in public class
}

Note: Each individual Dart file is a library by default. That means that the individual private components are not visible among the files.

An interesting concept of this language is the Named constructors. It’s a classic object constructor but you can define its name. If the constructor is named appropriately, you can deduce how the object is created from its name. In Java, for example, a similar construct could be created using a static method that would create and initialize the actual object.

class Person {
  String firstName;
  String surname;

  Person.fromJson(Map data) {
    firstName = data['first_name'];
    surname = data['surname'];
  }
}

main() {
  var person = new Person.fromJson({'first_name': 'Eman', 'surname': 'von Prag'});
  print(person.firstName); // Eman
  print(person.surname);   // von Prag
}

Functions

Dart also supports classical functions and because everything is an object, so are the functions. The main function is the main() function that is the app’s entry function, therefore every app will start with this function. You can nest the definitions of the individual functions and create, so called, Nested functions. Every nested function gains the context of the function in which it’s nested, it can see its local variables.

bool topLevel = true;

void main() {
  var insideMain = true;

  void nestedFunction() {
    var insideNestedFunction = true;

    void nestednestedFunction() {
      var insideNestedNestedFunction = true;

      assert(topLevel);
      assert(insideMain);
      assert(insideNestedFunction);
      assert(insideNestedNestedFunction);
    }

    //assert(insideNestedNestedFunction) translation error - unknown variable
  }
}

Null-aware operators

Similarly to Kotlin, Dart offers several operators that make notation of the Null Pointer safety code easier.

  • The android directory – here we will find the classic Android project. In the app module we can find the actual Android app that already contains a few generated files. These are for connecting the Flutter app to the classic Android app. All Android specific configurations, the applicationId definition for example, supported SDKs, or signing certificates will be modified here.
  • The ios directory – here we will find the typical iOS project. It already contains a few generated files used for connecting the Flutter app to the classic iOS app. All iOS specific configuration is to be done in this directory
  • The lib directory – the most important directory where the Flutter app files are stored. The main file here is main.dart containing the void main() function which is the input function for the Flutter app.
  • File pubspec.yaml – this file is for the Flutter app configuration. We also define the dependencies on external Flutter modules and Dart libraries in this file. For example, to add a library for detecting information about the device on which the app runs, we only need to add the following dependency.

Cascade

The cascade is for calling multiple methods on a single object. In Kotlin, a similar result is obtained by using the apply.

class Person {
  String firstName;
  String surname;
  String company;
}

main() {  
  var person = new Person()
    ..firstName = 'Bob'
    ..surname = 'Clever'
    ..company = 'eMan.cz';
}

Everything is a widget

You’ve probably heard that “everything is a widget” if you’ve read any article about Flutter. And yes, everything is actually a widget. Everything, even the app itself, is a widget. Thanks to this, every UI element can be addressed uniformly. There are two kinds of widgets:

  • The android directory – here we will find the classic Android project. In the app module we can find the actual Android app that already contains a few generated files. These are for connecting the Flutter app to the classic Android app. All Android specific configurations, the applicationId definition for example, supported SDKs, or signing certificates will be modified here.
  • The ios directory – here we will find the typical iOS project. It already contains a few generated files used for connecting the Flutter app to the classic iOS app. All iOS specific configuration is to be done in this directory
  • The lib directory – the most important directory where the Flutter app files are stored. The main file here is main.dart containing the void main() function which is the input function for the Flutter app.
  • File pubspec.yaml – this file is for the Flutter app configuration. We also define the dependencies on external Flutter modules and Dart libraries in this file. For example, to add a library for detecting information about the device on which the app runs, we only need to add the following dependency.

Source: https://flutter.io/technical-overview/

Every widget is very small because it manages only the activity it was created for and not any other. For example, the Text widget that displays text manages only the actual display of the text and doesn’t consider its position in connection to the other widgets nor has the ability to gather events (e.g. clicks). If you want it to have any other function, you simply add another widget that supports the function you need. In the end, developing a Flutter app results in putting the individual widgets together in order to get the desired functionality, which reminds us of building with Legos. And everyone loves Legos, right?! 😉

Flutter technology

Flutter does not use any native elements of the target platform. All elements were developed anew by Flutter developers in order to be suitable to the conventions of the target platform. Most of the app’s source code is translated directly into machine code of the target processor (via Android NDK on Android and via LLVM on iOS). That’s why you only need one source code for both platforms. And thanks to this the resulting app performance should be way faster, at least on Android.

You can see the individual framework layers on the diagram below. The dependency of the individual layers is from the top down; each layer depends on the layers below. The green part shows the main components of the Flutter framework. As you can see, Material (Android) and Cupertino (iOS) components are on the same level, making the platform-dependent development quite complicated as we saw in the last article. When looking over the Flutter documentation on its website, you can find a diagram showing the Material component to be dependent on Cupertino. That would solve the above mentioned issue. Unfortunately, when looking into the current source code of Flutter, it seems that the two components are at the same level.

The blue part shows components present in the Flutter core that are responsible for the actual operation of the app. The Skia component is responsible for the rendering of 2D graphics, Dart for compilation of the AOT parts of the code that couldn’t be compiled into the machine code directly, and the Text component manages the rendering of the text.

Edited from source: https://flutter.io/technical-overview/

Native interface

Flutter allows for calling a specific method of the native interface of the given platform using the, so called, Method channel. The Flutter app (client) sends a message with the request to the native part of the app (host). If the host knows the message, it manages the request and returns an answer. For this to be correctly served, both the client and the host have to implement the same message protocol.

Source: https://flutter.io/platform-channels/

As you can see from the diagram, using Method channel you can call any service of the native platform or a service that is offered by third party libraries. It’s also one of the ways to implement a service using the platform-specific services that Flutter doesn’t support. This is useful when, for example, you don’t have a library with the requested service available for Flutter but it exists as a library for Android and iOS.

Let’s see this in action using an example scenario when you want to find out the battery status of your device. As Flutter supports the creation of the platform-specific parts of the source code even in Kotlin for Android parts, and in Swift for iOS parts, we used these languages for our examples.

Client

The client sends a request about the battery status and displays the formatted result in the middle of the screen upon its reception. The message processing is asynchronous so the state of the battery can be displayed only after the result is received. In the example you can see one of the possible methods of processing such asynchronous calls.

An important part is the construction of the MethodChannel object that awaits in the parameter of the constructor for the name of the channel that is being created. On the host side, you can connect the host and the client using the this name. In order for the client and host to communicate, they need to use the same channel as well as send and expect to receive messages of the same type. The invokeMethod() method is used for defining the message type (in the parameter of the method).

The main.dart file in the lib directory should then look like this:

const platform = const MethodChannel('battery');

void main() {
  platform
      .invokeMethod('getBatteryLevel') // sends message with request 'getBatteryLevel'
      .then((result) => 'Battery level at $result %.')
      .catchError((error) => "Failed to get battery level: '${error.message}'.")
      .then((msg) => runApp(new Center(
            child: new Text(
              msg,
              textDirection: TextDirection.ltr,
            ),
          )));
}

Host

It is important that the host registers the handler on the same channel that was defined on the client’s side. After receiving a specific message type, that can be determined by call.method, you can perform the requested operation and save the result in the result object.

The MainActivy.kt file in the android/app/src/main/kotlin/package_name directory should then look like this:

class MainActivity : FlutterActivity() {
    private val CHANNEL = "battery"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        GeneratedPluginRegistrant.registerWith(this)

        MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->
            if (call.method == "getBatteryLevel") {
                val batteryLevel = getBatteryLevel()

                if (batteryLevel != -1) {
                    result.success(batteryLevel)
                } else {
                    result.error("UNAVAILABLE", "Battery level not available.", null)
                }
            } else {
                result.notImplemented()
            }
        }
    }

    private fun getBatteryLevel(): Int {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
            batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
        } else {
            val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
            intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
        }
    }
}

The AppDelegate.swift file in the ios/Runner directory should then look like this:

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        GeneratedPluginRegistrant.register(with: self);

        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController;
        let batteryChannel = FlutterMethodChannel.init(name: "battery", binaryMessenger: controller);
        batteryChannel.setMethodCallHandler({(call: FlutterMethodCall, result: FlutterResult) -> Void in
            if (call.method == "getBatteryLevel") {
                let batteryLevel : Int = self.getBatteryLevel();
                if(batteryLevel != -1) {
                    result(batteryLevel);
                } else {
                    result(FlutterError.init(code: "UNAVAILABLE", message: "Battery info unavailable", details: nil));
                }
            } else {
                result(FlutterMethodNotImplemented);
            }
        });

        return super.application(application, didFinishLaunchingWithOptions: launchOptions);
    }

    private func getBatteryLevel() -> Int {
        let device = UIDevice.current;
        device.isBatteryMonitoringEnabled = true;
        if (device.batteryState == UIDeviceBatteryState.unknown) {
            return -1;
        } else {
            return Int(device.batteryLevel * 100);
        }
    }
}

Conclusion

Today we discussed what to use and how to write when designing apps in Flutter. We presented the individual parts of the app and the individual parts of the Flutter framework. And we also tackled the communication with the native platform interface when calling platform-specific services. You can find the whole project at eMan’s GitHub.

Let’s Develop a Mobile App in Flutter (3/3)

We already know how to create dialogues and use Dart. In the last article of this series, we will finally get to the creation of a more complex app. We’ll also discuss how to tackle the support of a platform dependent app. Let’s do this!

In the previous article we had a look into the Flutter framework, introduced Dart, and showed how to call the native interface of the given platform. This article will present possible localization options of the app and we’ll also have a brief look at the creation of a screen layout. Then we’ll incorporate all gained knowledge into our app and, as the final step, we’ll connect it to Firebase. That’ll give us a skeleton of what’s today probably the most used software architecture in mobile development – a mobile app that, in the client’s role, gathers data from a server database.

We make widgets (Abstract Factory pattern)

The first article tackled the creation of a platform dependent dialogue. The presented solution used the corresponding graphic component based on the identification of the target platform. This solution is not ideal as it calls a code with a large number of if-then-else constructions for the identification of the platform and subsequent usage of the specific component. This issue can be fixed, for example, by using the Abstract Factory design pattern.

Abstract Factory in general

The Abstract Factory design pattern is a part of a creational design pattern group. Abstract Factory is used for the creation of similar products and eliminates the usage of new. A group of objects created by a factory is then clearly determined by the relevant factory.

  • The android directory – here we will find the classic Android project. In the app module we can find the actual Android app that already contains a few generated files. These are for connecting the Flutter app to the classic Android app. All Android specific configurations, the applicationId definition for example, supported SDKs, or signing certificates will be modified here.
  • The ios directory – here we will find the typical iOS project. It already contains a few generated files used for connecting the Flutter app to the classic iOS app. All iOS specific configuration is to be done in this directory
  • The lib directory – the most important directory where the Flutter app files are stored. The main file here is main.dart containing the void main() function which is the input function for the Flutter app.
  • File pubspec.yaml – this file is for the Flutter app configuration. We also define the dependencies on external Flutter modules and Dart libraries in this file. For example, to add a library for detecting information about the device on which the app runs, we only need to add the following dependency.

(Source: https://en.wikipedia.org/wiki/Abstract_factory_pattern)

To better understand it, picture this example: You’re building a house and want the windows and doors to be from a specific material. You can select from wood, plastic and aluminium, and the only restriction is that you can’t combine various materials.

  • The android directory – here we will find the classic Android project. In the app module we can find the actual Android app that already contains a few generated files. These are for connecting the Flutter app to the classic Android app. All Android specific configurations, the applicationId definition for example, supported SDKs, or signing certificates will be modified here.
  • The ios directory – here we will find the typical iOS project. It already contains a few generated files used for connecting the Flutter app to the classic iOS app. All iOS specific configuration is to be done in this directory
  • The lib directory – the most important directory where the Flutter app files are stored. The main file here is main.dart containing the void main() function which is the input function for the Flutter app.
  • File pubspec.yaml – this file is for the Flutter app configuration. We also define the dependencies on external Flutter modules and Dart libraries in this file. For example, to add a library for detecting information about the device on which the app runs, we only need to add the following dependency.

You make your order in a given factory and you know that the supplied products will be from the same material. This way you get rid of asking over and over again: “What material should the door/window be made of?” Instead, you just submit another order to the factory you selected at the beginning.

Abstract Factory in more detail

Our abstract factory will offer an interface for the creation of various graphics and other elements. The creation of the elements itself will be taken care of by two factories, one for Android and another for iOS style. But how do you determine what elements to create in the factory and what elements to create in a different way? We can approach this question from several angles:

  1. creating all graphic components
  2. creating all components that are in the material or cupertino package
  3. creating only the components that exist in both graphic versions

We decided to choose the second option as it’s a nice compromise between the other possibilities. Moreover, if an alternative component is implemented in the other package in the future, adding the component will be way easier. Then the source files should not contain any package:flutter/cupertino.dart or ipackage:flutter/material.dart import in other files than the ones containing the implementation of relevant factories.

When declaring the individual interface methods, it’s recommended to use the “Optional named parameters“ thanks to which the interface change (in a sense of adding a new parameter) influences only the specific factories implementing the given interface and leaves the places where the interface methods are called untouched.

Here is the creation of Scaffold and AppBar components as an example. You can find the whole interface in the widget_factory.dart file.

abstract class WidgetFactory {
  Widget createScaffold({PreferredSizeWidget appBar, Widget body});

  PreferredSizeWidget createAppBar({Widget title});
  ...
}

The implementation of the given factories that ensures the creation of the relevant component based on the requested style:

class _AndroidWidgetFactory implements WidgetFactory {
  @override
  Widget createScaffold({PreferredSizeWidget appBar, Widget body}) {
    return Scaffold(appBar: appBar, body: body);
  }

  @override
  PreferredSizeWidget createAppBar({Widget title}) {
    return AppBar(title: title);
  }
  ...
}

class _IOSWidgetFactory implements WidgetFactory {
  @override
  Widget createScaffold({PreferredSizeWidget appBar, Widget body}) {
    return CupertinoPageScaffold(navigationBar: appBar, child: body);
  }

  @override
  PreferredSizeWidget createAppBar({Widget title}) {
    return CupertinoNavigationBar(middle: title, backgroundColor: Colors.white);
  }
  ...
}

All the necessary classes are ready. We only need to add the logic for the initialization of the factory that will create the elements while the app is running. We used a Singleton design pattern that will make sure that the whole running app will use only one instance of the specific factory. Dart offers the “Factory constructor“ for the creation and a clean usage of Singletons. As we can see from the example, the logic for determining on what system the app is running is only in one place.

abstract class WidgetFactory {
  static WidgetFactory _instance;

  factory WidgetFactory() {
    if (_instance == null) {
      if (Platform.isAndroid) {
        _instance = _AndroidWidgetFactory();
      } else if (Platform.isIOS) {
        _instance = _IOSWidgetFactory();
      } else {
        throw UnsupportedError('Unsupported target platform.');
      }
    }
    return _instance;
  }
  ...
}

// using
WidgetFactory().createScaffold(...);

Note: We could use a static method getInstance() for the creation of the Factory construction for returning the class instance in Java.

Pros and cons

  • The android directory – here we will find the classic Android project. In the app module we can find the actual Android app that already contains a few generated files. These are for connecting the Flutter app to the classic Android app. All Android specific configurations, the applicationId definition for example, supported SDKs, or signing certificates will be modified here.
  • The ios directory – here we will find the typical iOS project. It already contains a few generated files used for connecting the Flutter app to the classic iOS app. All iOS specific configuration is to be done in this directory
  • The lib directory – the most important directory where the Flutter app files are stored. The main file here is main.dart containing the void main() function which is the input function for the Flutter app.
  • File pubspec.yaml – this file is for the Flutter app configuration. We also define the dependencies on external Flutter modules and Dart libraries in this file. For example, to add a library for detecting information about the device on which the app runs, we only need to add the following dependency.

Localization of the app

Today’s standard is that apps are offered in various language mutations in order to attract as wide a spectrum of users as possible. And Flutter is one of them, offering several options on how to get the required properties. Using the intl package for the support of localization is one of the possible options. The package adds the support for localization to any project written in Dart. It establishes a special construction for getting the string’s definitions in the project that ensures the usage of a correct language mutation. Based on this construction and using a special tool, files with corresponding strings are generated for each language mutation that supplement the translation. These are then added to the final app. The second option we’ll present is defining the translation directly in the source code.

We’ll add a new dependency to the pubspec.yaml file that will provide the support for localization.

dependencies:
  flutter_localizations:
    sdk: flutter
  ...

In order to add support for any language to our app, we need to add two classes, as shown in the example below. The _EventsLocalizationsDelegate class is for the loading and storing of the EventsLocalizations object with the relevant, supported localization. Both classes are quite usual and they’ll be similar in every project. The only thing that changes is the values defined in the _localizedValues attribute. It’s a master map where the keys of the first map are marks according to the IANA register.

enum StringId {
  appTitle,
  homeScreenTitle,
  ...
}

class EventsLocalizations {
  static const LocalizationsDelegate<EventsLocalizations> delegate = const _EventsLocalizationsDelegate();

  static const Map<String, Map<StringId, String>> _localizedValues = {
    'en': {
      StringId.appTitle: 'Eman\'s events',
      StringId.homeScreenTitle: 'Events',
      ...
    },
    'cs': {
      StringId.appTitle: 'Eman\'s events',
      StringId.homeScreenTitle: 'Akce',
      ...
    },
    ...
  };

  static final Iterable<Locale> supportedLocales = _localizedValues.keys.map((languageCode) => Locale(languageCode));

  static EventsLocalizations of(BuildContext context) => Localizations.of<EventsLocalizations>(context, EventsLocalizations);

  final Locale _locale;

  EventsLocalizations(this._locale);

  String get appTitle => _localizedValues[_locale.languageCode][StringId.appTitle];
  String get homeScreenTitle => _localizedValues[_locale.languageCode][StringId.homeScreenTitle];
  // or
  String getString(StringId stringId) => _localizedValues[_locale.languageCode][stringId];
}

class _EventsLocalizationsDelegate extends LocalizationsDelegate<EventsLocalizations> {
  const _EventsLocalizationsDelegate();

  @override
  bool isSupported(Locale locale) => EventsLocalizations._localizedValues.containsKey(locale.languageCode);

  @override
  Future<EventsLocalizations> load(Locale locale) => SynchronousFuture<EventsLocalizations>(EventsLocalizations(locale));

  @override
  bool shouldReload(LocalizationsDelegate<EventsLocalizations> old) => false;
}

The last thing to do is to specify which language mutations are supported by the app (the supportedLocales parameter of the app’s widget) and register the delegates for all components that’ll be responsible for the correct localization (the localizationsDelegates field of the app’s widget).

class EventsApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: [
        EventsLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: EventsLocalizations.supportedLocales,
      ...
    );
  }
}

Then to get a localized string we’ll use one of the following methods based on what localization class interface we selected.

EventsLocalizations.of(context).appTitle;
// or
EventsLocalizations.of(context).getString(StringId.appTitle);

Note: The current Material Components version does not support Czech, it introduces an error while rendering widgets.

Creating layouts

As previously stated, the layout creation is simply a puzzle. You just put together simple widgets, thus creating a widget that looks and does what was expected of it from the beginning. The following example shows the creation of ListView and its elements and we’ll also take a look at the usage of a few basic layouts. If you’d like to create more complex layouts, take a look at the Flutter webpage for some ideas to get you started.

class EventListState extends State<EventList> {
  final _events = List<Event>();

  @override
  void initState() {
    super.initState();
    _events.addAll(generateWordPairs().take(10).map((pair) => Event(
          pair.asPascalCase,
          DateTime.now(),
          lorem.createParagraph(),
        )));
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
        padding: const EdgeInsets.all(20.0),
        itemCount: _events.length * 2,
        itemBuilder: (context, index) {
          if (index.isOdd) {
            return WidgetFactory().createDivider();
          } else {
            return _buildItem(_events[index ~/ 2]);
          }
        });
  }

  Widget _buildItem(Event event) {
    return GestureDetector(
      child: Container(
        decoration: BoxDecoration(color: Colors.transparent),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Container(
              padding: const EdgeInsets.only(bottom: 8.0),
              child: Text(
                event.name,
                style: TextStyle(fontSize: 20.0, color: Colors.black),
              ),
            ),
            Text(
              format.format(event.dateTime),
              style: TextStyle(
                fontSize: 16.0,
                color: Colors.grey[500],
              ),
            ),
          ],
        ),
      ),
      onTap: () => _itemClicked(event),
    );
  }
  ...
}

We’ll focus only on the _buildItem method that creates the layout of a single item of the list.

  • The android directory – here we will find the classic Android project. In the app module we can find the actual Android app that already contains a few generated files. These are for connecting the Flutter app to the classic Android app. All Android specific configurations, the applicationId definition for example, supported SDKs, or signing certificates will be modified here.
  • The ios directory – here we will find the typical iOS project. It already contains a few generated files used for connecting the Flutter app to the classic iOS app. All iOS specific configuration is to be done in this directory
  • The lib directory – the most important directory where the Flutter app files are stored. The main file here is main.dart containing the void main() function which is the input function for the Flutter app.
  • File pubspec.yaml – this file is for the Flutter app configuration. We also define the dependencies on external Flutter modules and Dart libraries in this file. For example, to add a library for detecting information about the device on which the app runs, we only need to add the following dependency.

You can picture the layout as a tree where every parent nod determines the behaviour or manner of the rendering of its children.

Cloud Firestore database

Cloud Firestore is a Document Store database; it’s the successor to the original Firebase real-time database. We’ll create a Firestore database for our example, with a collection named events containing several documents with the following structure:

name: "...",
date_time: "...",
description: "...",

We won’t go into details of the actual Cloud Firestore database creation and its addition to the project. For more details on this process, please have a look at this guide from Google. After the actual configuration, adding it to the project is a piece of cake. In the previous example, the initState method created a list of events containing random dates that we will now switch for dates from the database. We’ll get the events collection from an instance of the database and all documents that it contains will be mapped by the Event model class. In the following example we regard the events collection as a data stream and, using the listen method, we react to any change of the data. Feel free to try for yourself how the app reacts to you changing the data in the database.

class EventListState extends State<EventList> {
  ...
  @override
  void initState() {
    Firestore.instance
        .collection('events')
        .orderBy('date_time')
        .snapshots()
        .listen((event) => setState(() {
              _events.clear();
              _events.addAll(event.documents.map((snapshot) => Event(
                    snapshot.data['name'],
                    DateTime.parse('${snapshot.data['date_time']}z'),
                    snapshot.data['description'],
                  )));
            }));
  ...
}

Conclusion

In this article we presented how to tackle the issue of keeping the style for a given platform, and we created a support for a multi-language app. Then we created a simple layout and connected the app to the Firestore database. You can find the whole project that we have been very secretly creating here, one part at a time, at our GitHub, where you can also see that the project’s history follows this article.

#mobile-apps #flutter #ios #android

What is GEEK

Buddha Community

Let’s Develop a Mobile App in Flutter

Very Interesting Post! Thanks For sharing this information about Develop a Mobile App in Flutter

Fredy  Larson

Fredy Larson

1595059664

How long does it take to develop/build an app?

With more of us using smartphones, the popularity of mobile applications has exploded. In the digital era, the number of people looking for products and services online is growing rapidly. Smartphone owners look for mobile applications that give them quick access to companies’ products and services. As a result, mobile apps provide customers with a lot of benefits in just one device.

Likewise, companies use mobile apps to increase customer loyalty and improve their services. Mobile Developers are in high demand as companies use apps not only to create brand awareness but also to gather information. For that reason, mobile apps are used as tools to collect valuable data from customers to help companies improve their offer.

There are many types of mobile applications, each with its own advantages. For example, native apps perform better, while web apps don’t need to be customized for the platform or operating system (OS). Likewise, hybrid apps provide users with comfortable user experience. However, you may be wondering how long it takes to develop an app.

To give you an idea of how long the app development process takes, here’s a short guide.

App Idea & Research

app-idea-research

_Average time spent: two to five weeks _

This is the initial stage and a crucial step in setting the project in the right direction. In this stage, you brainstorm ideas and select the best one. Apart from that, you’ll need to do some research to see if your idea is viable. Remember that coming up with an idea is easy; the hard part is to make it a reality.

All your ideas may seem viable, but you still have to run some tests to keep it as real as possible. For that reason, when Web Developers are building a web app, they analyze the available ideas to see which one is the best match for the targeted audience.

Targeting the right audience is crucial when you are developing an app. It saves time when shaping the app in the right direction as you have a clear set of objectives. Likewise, analyzing how the app affects the market is essential. During the research process, App Developers must gather information about potential competitors and threats. This helps the app owners develop strategies to tackle difficulties that come up after the launch.

The research process can take several weeks, but it determines how successful your app can be. For that reason, you must take your time to know all the weaknesses and strengths of the competitors, possible app strategies, and targeted audience.

The outcomes of this stage are app prototypes and the minimum feasible product.

#android app #frontend #ios app #minimum viable product (mvp) #mobile app development #web development #android app development #app development #app development for ios and android #app development process #ios and android app development #ios app development #stages in app development

Hire Dedicated Flutter App Developer USA| Flutter App Developers

Hire Flutter App Developers: WebClues Infotech is a Flutter App Development company. Our Flutter mobile app development team can create cross-platform apps for different industry verticals. Our Flutter developers will help you extend your business’s scope by developing enhanced functionality and a feature-rich app. To provide a rich user experience to your users, hire dedicated Flutter app developers from WebClues Infotech today!

#hire flutter app developers #hire dedicated flutter app developer usa #hire flutter app developer usa #hire dedicated flutter app developer #hire flutter developer #flutter app development company

Aria Barnes

Aria Barnes

1626080347

Tips to Accelerate App Project’s Process with Mobile App Development Company

Mobile app development has attained an incredible height in the last few years. It will continue to achieve more in the following years. Many startup owners have ideas to lead their business at the forefront, but they lack in executing them due to guidance.

If you are also one of them and are hesitant to discuss. In that case, mobile app development companies in India help you to execute the idea. You will get full support as you can share your business objectives in detail with them.

Do you know? According to a report, the Mobile app economy will reach up to $6.3 trillion by the end of 2021.

While communicating the idea, you need to focus on critical perspectives to create a better mobile application. In the blog, I have penned down all vital perspectives. By going through these, you will have a clear vision of how to accelerate the app discussion with mobile applications development companies in India. .

Table of Contents

  • Mention the Requirements and Expectations Clearly
  • Set up the Project Delivery Deadline
  • Mobile App Testing at Development Stages
  • Development Discussions at Regular Interval
  • Brainstorm your App Idea
  • Define Your Minimum Viable Product
  • Focus on UX/UI Design
  • Mobile App Backend Development
  • Mobile App Frontend Development
  • Testing of the Developed Application
  • App Deployment and Maintenance
  • Summing Up!

Let’s focus on the discussion perspectives with Mobile app development companies.

Read more here: Tips to Accelerate App Project’s Process with Mobile App Development Company

 

#mobile app development #mobile app development company #mobile app development company in india #mobile app development companies india #top mobile app development company #android app development company

Google's Flutter 1.20 stable announced with new features - Navoki

Flutter Google cross-platform UI framework has released a new version 1.20 stable.

Flutter is Google’s UI framework to make apps for Android, iOS, Web, Windows, Mac, Linux, and Fuchsia OS. Since the last 2 years, the flutter Framework has already achieved popularity among mobile developers to develop Android and iOS apps. In the last few releases, Flutter also added the support of making web applications and desktop applications.

Last month they introduced the support of the Linux desktop app that can be distributed through Canonical Snap Store(Snapcraft), this enables the developers to publish there Linux desktop app for their users and publish on Snap Store.  If you want to learn how to Publish Flutter Desktop app in Snap Store that here is the tutorial.

Flutter 1.20 Framework is built on Google’s made Dart programming language that is a cross-platform language providing native performance, new UI widgets, and other more features for the developer usage.

Here are the few key points of this release:

Performance improvements for Flutter and Dart

In this release, they have got multiple performance improvements in the Dart language itself. A new improvement is to reduce the app size in the release versions of the app. Another performance improvement is to reduce junk in the display of app animation by using the warm-up phase.

sksl_warm-up

If your app is junk information during the first run then the Skia Shading Language shader provides for pre-compilation as part of your app’s build. This can speed it up by more than 2x.

Added a better support of mouse cursors for web and desktop flutter app,. Now many widgets will show cursor on top of them or you can specify the type of supported cursor you want.

Autofill for mobile text fields

Autofill was already supported in native applications now its been added to the Flutter SDK. Now prefilled information stored by your OS can be used for autofill in the application. This feature will be available soon on the flutter web.

flutter_autofill

A new widget for interaction

InteractiveViewer is a new widget design for common interactions in your app like pan, zoom drag and drop for resizing the widget. Informations on this you can check more on this API documentation where you can try this widget on the DartPad. In this release, drag-drop has more features added like you can know precisely where the drop happened and get the position.

Updated Material Slider, RangeSlider, TimePicker, and DatePicker

In this new release, there are many pre-existing widgets that were updated to match the latest material guidelines, these updates include better interaction with Slider and RangeSliderDatePicker with support for date range and time picker with the new style.

flutter_DatePicker

New pubspec.yaml format

Other than these widget updates there is some update within the project also like in pubspec.yaml file format. If you are a flutter plugin publisher then your old pubspec.yaml  is no longer supported to publish a plugin as the older format does not specify for which platform plugin you are making. All existing plugin will continue to work with flutter apps but you should make a plugin update as soon as possible.

Preview of embedded Dart DevTools in Visual Studio Code

Visual Studio code flutter extension got an update in this release. You get a preview of new features where you can analyze that Dev tools in your coding workspace. Enable this feature in your vs code by _dart.previewEmbeddedDevTools_setting. Dart DevTools menu you can choose your favorite page embed on your code workspace.

Network tracking

The updated the Dev tools comes with the network page that enables network profiling. You can track the timings and other information like status and content type of your** network calls** within your app. You can also monitor gRPC traffic.

Generate type-safe platform channels for platform interop

Pigeon is a command-line tool that will generate types of safe platform channels without adding additional dependencies. With this instead of manually matching method strings on platform channel and serializing arguments, you can invoke native class and pass nonprimitive data objects by directly calling the Dartmethod.

There is still a long list of updates in the new version of Flutter 1.2 that we cannot cover in this blog. You can get more details you can visit the official site to know more. Also, you can subscribe to the Navoki newsletter to get updates on these features and upcoming new updates and lessons. In upcoming new versions, we might see more new features and improvements.

You can get more free Flutter tutorials you can follow these courses:

#dart #developers #flutter #app developed #dart devtools in visual studio code #firebase local emulator suite in flutter #flutter autofill #flutter date picker #flutter desktop linux app build and publish on snapcraft store #flutter pigeon #flutter range slider #flutter slider #flutter time picker #flutter tutorial #flutter widget #google flutter #linux #navoki #pubspec format #setup flutter desktop on windows

Jones Brianna

Jones Brianna

1614154249

List Of The Top Pittsburgh Mobile App Development Companies

https://clutch.co/app-developers/pittsburgh
Let’s look at the list of top list of the top Pittsburgh mobile app development companies which are known for providing top-notch services globally. They are great developers who provide quality services for all your needs.

#mobile app developers #mobile app development services #mobile app development #mobile app developers #mobile apps #mobile app development solutions