Author: saqibkhan

  • Dart 2 (2018)

    The introduction of Dart 2 brought major changes, including a more powerful type system and improved performance. Dart 2 emphasized sound null safety, making it easier for developers to avoid null-related bugs.

  • Creating Android Platform-Specific Code

    In this section, we are going to see how we can write custom platform-specific code in Flutter. Flutter is an excellent framework, which provides a mechanism to handle/access platform-specific features. This feature allows the developer to extend the functionality of the Flutter framework. Some of the essential platform-specific functionality that can be accessed easily through the framework are camera, battery level, browser, etc.

    Flutter uses a flexible system to call platform-specific API either available on Android in Java or Kotlin code, or in Objective-C or Swift code on iOS. The general idea of accessing the platform-specific code in Flutter is through the messaging protocol. The messages are passed between the client (UI) and Host (Platform) using the common message channel. It means the clients sends a message to the Host by using this message channel. Next, the Host listens on that message channel, receives the message, does the appropriate functionality, and finally returns the result to the client.

    The following block diagram shows the appropriate platform-specific code architecture.

    Flutter Creating Android Platform-Specific Code

    The messaging channel uses a standard message codec (StandardMessageCodec class), which supports efficient binary serialization of JSON like values, such as string, Boolean, numbers, byte buffers, and List and Maps, etc. The serialization and deserialization of these values work automatically between clients and hosts when you send and receive values.

    The following table shows how dart values are received on Android and iOS platform and vice-versa:

    DartAndroidiOS
    nullnullnil (NSNull when nested)
    booljava.lang.BooleanNSNumber numberWithBool:
    intjava.lang.IntegerNSNumber numberWithInt:
    doublejava.lang.DoubleNSNumber numberWithDouble:
    Stringjava.lang.StringNSString:
    Uint8Listbyte[]FlutterStandardTypedData typedDataWithBytes:
    Int32Listint[]FlutterStandardTypedData typedDataWithInt32:
    Int64Listlong[]FlutterStandardTypedData typedDataWithInt64:
    Float64Listdouble[]FlutterStandardTypedData typedDataWithFloat64:
    Listjava.util.ArrayListNSArray
    Mapjava.util.HashMApNSDictionary

    Let us create a simple application to demonstrate how to call a platform-specific API to open a browser. To do this, we need to create a Flutter project in Android Studio and insert the following code in the main.dart file.

    import 'package:flutter/material.dart';  
    
    import 'dart:async';  
    
    import 'package:flutter/services.dart';  
    
      
    
    void main() => runApp(MyApp());  
    
    class MyApp extends StatelessWidget {  
    
      @override  
    
      Widget build(BuildContext context) {  
    
        return MaterialApp(  
    
          title: 'Flutter DemoApplication',  
    
          theme: ThemeData(  
    
            primarySwatch: Colors.green,  
    
          ),  
    
          home: MyHomePage(  
    
              title: 'Flutter Platform Specific Page'  
    
          ),  
    
        );  
    
      }  
    
    }  
    
    class MyHomePage extends StatelessWidget {  
    
      MyHomePage({Key key, this.title}) : super(key: key);  
    
      final String title;  
    
      static const platform = const MethodChannel('flutterplugins.javatpoint.com/browser');  
    
      Future<void> _openBrowser() async {  
    
        try {  
    
          final int result = await platform.invokeMethod('openBrowser', <String, String>{  
    
            'url': "https://www.javatpoint.com"  
    
          });  
    
        }  
    
        on PlatformException catch (e) {  
    
          // Unable to open the browser  
    
          print(e);  
    
        }  
    
      }  
    
      @override  
    
      Widget build(BuildContext context) {  
    
        return Scaffold(  
    
          appBar: AppBar(  
    
            title: Text(this.title),  
    
          ),  
    
          body: Center(  
    
            child: RaisedButton(  
    
              child: Text('Click Here'),  
    
              onPressed: _openBrowser,  
    
            ),  
    
          ),  
    
        );  
    
      }  
    
    } 

      In the above file, we have imported a service.dart file, which includes the functionality to invoke the platform-specific code. In the MyHomePage widget, we have created a message channel and write a method _openBrowser to invoke the platform-specific code.

      Future<void> _openBrowser() async {  
      
        try {  
      
          final int result = await platform.invokeMethod('openBrowser', <String, String>{  
      
            'url': "https://www.javatpoint.com"  
      
          });  
      
        }  
      
        on PlatformException catch (e) {  
      
          // Unable to open the browser   
      
       print(e);  
      
        }  
      
      } 

        Finally, we have created a button to open the browser.

        Now, we need to provide the custom platform-specific implementation. To do this, navigate to the Android folder of your Flutter project and select the Java or Kotlin file and insert the following code into the MainActivity file. This code might change according to the Java or Kotlin language.

        package com.javatpoint.flutterplugins.flutter_demoapplication  
        
          
        
        import android.app.Activity  
        
        import android.content.Intent  
        
        import android.net.Uri  
        
        import android.os.Bundle  
        
        import io.flutter.app.FlutterActivity  
        
        import io.flutter.plugin.common.MethodCall  
        
        import io.flutter.plugin.common.MethodChannel  
        
        import io.flutter.plugin.common.MethodChannel.MethodCallHandler  
        
        import io.flutter.plugin.common.MethodChannel.Result  
        
        import io.flutter.plugins.GeneratedPluginRegistrant  
        
          
        
         class MainActivity:FlutterActivity() {  
        
            override fun onCreate(savedInstanceState:Bundle?) {  
        
                super.onCreate(savedInstanceState)  
        
                GeneratedPluginRegistrant.registerWith(this)  
        
                MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->  
        
                val url = call.argument<String>("url")  
        
                if (call.method == "openBrowser") {  
        
                    openBrowser(call, result, url)  
        
                } else {  
        
                    result.notImplemented()  
        
                }  
        
            }  
        
         }  
        
         private fun openBrowser(call:MethodCall, result:Result, url:String?) {  
        
            val activity = this  
        
            if (activity == null)  
        
            {  
        
                result.error("UNAVAILABLE", "It cannot open the browser without foreground activity", null)  
        
                return  
        
            }  
        
            val intent = Intent(Intent.ACTION_VIEW)  
        
            intent.data = Uri.parse(url)  
        
            activity!!.startActivity(intent)  
        
            result.success(true as Any)  
        
         }  
        
          
        
         companion object {  
        
            private val CHANNEL = "flutterplugins.javatpoint.com/browser"  
        
         }  
        
        }  

          In the MainActivity.kt file, we have created a method openBrowser() to open the browser.

          private fun openBrowser(call:MethodCall, result:Result, url:String?) {  
          
              val activity = this  
          
              if (activity == null)  
          
              {  
          
                  result.error("UNAVAILABLE", "It cannot open the browser without foreground activity", null)  
          
                  return  
          
              }  
          
              val intent = Intent(Intent.ACTION_VIEW)  
          
              intent.data = Uri.parse(url)  
          
              activity!!.startActivity(intent)  
          
              result.success(true as Any)  
          
           }  

            Output

            Now, run the app in your android studio, you will get the following output. When you click on the button Click Here, you can see that the browser home page screen is launched.

            Flutter Creating Android Platform-Specific Code
            Flutter Creating Android Platform-Specific Code
          1. Dart SDK (2013)

            In 2013, the Dart SDK was made available, allowing developers to compile Dart code to JavaScript. This was a significant step for Dart as it enabled developers to use Dart in existing web applications.

          2. First Release (2011)

            Dart 1.0 was released in November 2011. This version included features such as a strong static type system, classes, interfaces, and a garbage collector. The language was designed for both client-side and server-side development.

          3. Initial Development (2010)

            Dart was first introduced by Google in 2010 as a response to the challenges of web development, particularly with JavaScript’s limitations. The goal was to create a language that was easier to use and offered better performance.

          4. Navigation and Routing

            Navigation and routing are some of the core concepts of all mobile application, which allows the user to move between different pages. We know that every mobile application contains several screens for displaying different types of information. For example, an app can have a screen that contains various products. When the user taps on that product, immediately it will display detailed information about that product.

            In Flutter, the screens and pages are known as routes, and these routes are just a widget. In Android, a route is similar to an Activity, whereas, in iOS, it is equivalent to a ViewController.

            In any mobile app, navigating to different pages defines the workflow of the application, and the way to handle the navigation is known as routing. Flutter provides a basic routing class MaterialPageRoute and two methods Navigator.push() and Navigator.pop() that shows how to navigate between two routes. The following steps are required to start navigation in your application.

            Step 1: First, you need to create two routes.

            Step 2: Then, navigate to one route from another route by using the Navigator.push() method.

            Step 3: Finally, navigate to the first route by using the Navigator.pop() method.

            Let us take a simple example to understand the navigation between two routes:

            Create two routes

            Here, we are going to create two routes for navigation. In both routes, we have created only a single button. When we tap the button on the first page, it will navigate to the second page. Again, when we tap the button on the second page, it will return to the first page. The below code snippet creates two routes in the Flutter application.

            class FirstRoute extends StatelessWidget {  
            
              @override  
            
              Widget build(BuildContext context) {  
            
                return Scaffold(  
            
                  appBar: AppBar(  
            
                    title: Text('First Route'),  
            
                  ),  
            
                  body: Center(  
            
                    child: RaisedButton(  
            
                      child: Text('Open route'),  
            
                      onPressed: () {  
            
                        // Navigate to second route when tapped.  
            
                      },  
            
                    ),  
            
                  ),  
            
                );  
            
              }  
            
            }  
            
              
            
            class SecondRoute extends StatelessWidget {  
            
              @override  
            
              Widget build(BuildContext context) {  
            
                return Scaffold(  
            
                  appBar: AppBar(  
            
                    title: Text("Second Route"),  
            
                  ),  
            
                  body: Center(  
            
                    child: RaisedButton(  
            
                      onPressed: () {  
            
                        // Navigate back to first route when tapped.  
            
                      },  
            
                      child: Text('Go back!'),  
            
                    ),  
            
                  ),  
            
                );  
            
              }  
            
            } 

              Navigate to the second route using Navigator.push() method

              The Navigator.push() method is used to navigate/switch to a new route/page/screen. Here, the push() method adds a page/route on the stack and then manage it by using the Navigator. Again we use MaterialPageRoute class that allows transition between the routes using a platform-specific animation. The below code explain the use of the Navigator.push() method.

              // Within the FirstRoute widget  
              
              onPressed: () {  
              
                Navigator.push(  
              
                  context,  
              
                  MaterialPageRoute(builder: (context) => SecondRoute()),  
              
                );  
              
              } 

                Return to the first route using Navigator.pop() method

                Now, we need to use Navigator.pop() method to close the second route and return to the first route. The pop() method allows us to remove the current route from the stack, which is managed by the Navigator.To implement a return to the original route, we need to update the onPressed() callback method in the SecondRoute widget as below code snippet:

                // Within the SecondRoute widget  
                
                onPressed: () {  
                
                  Navigator.pop(context);  
                
                }

                Now, let us see the full code to implement the navigation between two routes. First, create a Flutter project and insert the following code in the main.dart file.

                import 'package:flutter/material.dart';  
                
                  
                
                void main() {  
                
                  runApp(MaterialApp(  
                
                    title: 'Flutter Navigation',  
                
                    theme: ThemeData(  
                
                      // This is the theme of your application.  
                
                      primarySwatch: Colors.green,  
                
                    ),  
                
                    home: FirstRoute(),  
                
                  ));  
                
                }  
                
                  
                
                class FirstRoute extends StatelessWidget {  
                
                  @override  
                
                  Widget build(BuildContext context) {  
                
                    return Scaffold(  
                
                      appBar: AppBar(  
                
                        title: Text('First Screen'),  
                
                      ),  
                
                      body: Center(  
                
                        child: RaisedButton(  
                
                          child: Text('Click Here'),  
                
                          color: Colors.orangeAccent,  
                
                          onPressed: () {  
                
                            Navigator.push(  
                
                              context,  
                
                              MaterialPageRoute(builder: (context) => SecondRoute()),  
                
                            );  
                
                          },  
                
                        ),  
                
                      ),  
                
                    );  
                
                  }  
                
                }  
                
                  
                
                class SecondRoute extends StatelessWidget {  
                
                  @override  
                
                  Widget build(BuildContext context) {  
                
                    return Scaffold(  
                
                      appBar: AppBar(  
                
                        title: Text("Second Screen"),  
                
                      ),  
                
                      body: Center(  
                
                        child: RaisedButton(  
                
                          color: Colors.blueGrey,  
                
                          onPressed: () {  
                
                            Navigator.pop(context);  
                
                          },  
                
                          child: Text('Go back'),  
                
                        ),  
                
                      ),  
                
                    );  
                
                  }  
                
                } 

                  Output

                  When you run the project in the Android Studio, you will get the following screen in your emulator. It is the first screen that contains only a single button.

                  Flutter Navigation and Routing

                  Click the button Click Here, and you will navigate to a second screen as below image. Next, when you click on the button Go Back, you will return to the first page.

                  Flutter Navigation and Routing

                  Navigation with Named Routes

                  We have learned how to navigate to a new screen by creating a new route and manage it by using the Navigator. The Navigator maintains the stack-based history of routes. If there is a need to navigate to the same screen in many parts of the app, this approach is not beneficial because it results in code duplication. The solution to this problem can be removed by defining the named routes and can use the named routes for navigation.

                  We can work with named routes by using the Navigator.pushNamed() function. This function takes two required arguments (build context and string) and one optional argument. Also, we know about the MaterialPageRoute, which is responsible for page transition. If we do not use this, then it is difficult to change the page.

                  The following steps are necessary, which demonstrate how to use named routes.

                  Step 1: First, we need to create two screens. The following code creates the two screens in our app.

                   class HomeScreen extends StatelessWidget {  
                  
                    @override  
                  
                    Widget build(BuildContext context) {  
                  
                      return Scaffold(  
                  
                        appBar: AppBar(  
                  
                          title: Text('Home Screen'),  
                  
                        ),  
                  
                        body: Center(  
                  
                          child: RaisedButton(  
                  
                            child: Text('Click Here'),  
                  
                            color: Colors.orangeAccent,  
                  
                            onPressed: () {  
                  
                              //  
                  
                            },  
                  
                          ),  
                  
                        ),  
                  
                      );  
                  
                    }  
                  
                  }  
                  
                    
                  
                  class SecondScreen extends StatelessWidget {  
                  
                    @override  
                  
                    Widget build(BuildContext context) {  
                  
                      return Scaffold(  
                  
                        appBar: AppBar(  
                  
                          title: Text("Second Screen"),  
                  
                        ),  
                  
                        body: Center(  
                  
                          child: RaisedButton(  
                  
                            color: Colors.blueGrey,  
                  
                            onPressed: () {  
                  
                              //  
                  
                            },  
                  
                            child: Text('Go back!'),  
                  
                          ),  
                  
                        ),  
                  
                      );  
                  
                    }  
                  
                  }

                    Step 2: Define the routes.

                    In this step, we have to define the routes. The MaterialApp constructor is responsible for defining the initial route and other routes themselves. Here the initial route tells the start of the page and routes property defines the available named routes and widgets. The following code explains it more clearly.

                    MaterialApp(  
                    
                      title: 'Named Route Navigation',  
                    
                      theme: ThemeData(  
                    
                        // This is the theme of your application.  
                    
                        primarySwatch: Colors.green,  
                    
                      ),  
                    
                      // It start the app with the "/" named route. In this case, the app starts  
                    
                      // on the HomeScreen widget.  
                    
                      initialRoute: '/',  
                    
                      routes: {  
                    
                        // When navigating to the "/" route, build the HomeScreen widget.  
                    
                        '/': (context) => HomeScreen(),  
                    
                        // When navigating to the "/second" route, build the SecondScreen widget.  
                    
                        '/second': (context) => SecondScreen(),  
                    
                      },  
                    
                    ));  

                      Step 3: Navigate to the second screen using the Navigator.pushNamed() function.

                      In this step, we need to call Navigator.pushNamed() method for navigation. For this, we need to update an onPressed() callback in the build method of HomeScreen like below code snippets.

                      onPressed: () {  
                      
                        // Navigate to the second screen by using the named route.  
                      
                        Navigator.pushNamed(context, '/second');  
                      
                      }  

                        Step 4: Use a Navigator.pop() function to return to the first screen.

                        It is the final step, where we will use Navigator.pop() method to return on the first screen.

                        onPressed: () {  
                        
                          Navigator.pushNamed(context, '/second');  
                        
                        },  

                          Let us see the full code of the above explanation in the Flutter project and run it in the emulator to get the output.

                          import 'package:flutter/material.dart';  
                          
                            
                          
                          void main() {  
                          
                            runApp( MaterialApp(  
                          
                              title: 'Named Route Navigation',  
                          
                              theme: ThemeData(  
                          
                                // This is the theme of your application.  
                          
                                primarySwatch: Colors.green,  
                          
                              ),  
                          
                              // Start the app with the "/" named route. In this case, the app starts  
                          
                              // on the FirstScreen widget.  
                          
                              initialRoute: '/',  
                          
                              routes: {  
                          
                                // When navigating to the "/" route, build the FirstScreen widget.  
                          
                                '/': (context) => HomeScreen(),  
                          
                                // When navigating to the "/second" route, build the SecondScreen widget.  
                          
                                '/second': (context) => SecondScreen(),  
                          
                              },  
                          
                            ));  
                          
                          }  
                          
                            
                          
                          class HomeScreen extends StatelessWidget {  
                          
                            @override  
                          
                            Widget build(BuildContext context) {  
                          
                              return Scaffold(  
                          
                                appBar: AppBar(  
                          
                                  title: Text('Home Screen'),  
                          
                                ),  
                          
                                body: Center(  
                          
                                  child: RaisedButton(  
                          
                                    child: Text('Click Here'),  
                          
                                    color: Colors.orangeAccent,  
                          
                                    onPressed: () {  
                          
                                      Navigator.pushNamed(context, '/second');  
                          
                                    },  
                          
                                  ),  
                          
                                ),  
                          
                              );  
                          
                            }  
                          
                          }  
                          
                            
                          
                          class SecondScreen extends StatelessWidget {  
                          
                            @override  
                          
                            Widget build(BuildContext context) {  
                          
                              return Scaffold(  
                          
                                appBar: AppBar(  
                          
                                  title: Text("Second Screen"),  
                          
                                ),  
                          
                                body: Center(  
                          
                                  child: RaisedButton(  
                          
                                    color: Colors.blueGrey,  
                          
                                    onPressed: () {  
                          
                                      Navigator.pop(context);  
                          
                                    },  
                          
                                    child: Text('Go back!'),  
                          
                                  ),  
                          
                                ),  
                          
                              );  
                          
                            }  
                          
                          }

                          Output

                          Flutter Navigation and Routing

                          Click the button Click Here, and you will navigate to a second screen as below image. Next, when you click on the button Go Back, it will return to the Home Page.

                          Flutter Navigation and Routing
                        1. Flutter Dependency

                          Dart’s strong integration with Flutter can be a disadvantage if developers prefer other frameworks or platforms. Using Dart outside the Flutter ecosystem may not offer the same level of support and resources.

                        2. Learning Curve for Existing Developers

                          Developers with experience in other languages may need to invest time in learning Dart’s syntax, features, and best practices. Adapting to Dart’s ecosystem and tools can also pose a learning curve.

                        3. Limited Adoption

                          While Dart has gained popularity, it still lags behind other programming languages like JavaScript in terms of adoption and community size. This may result in fewer resources and community support compared to more widely adopted languages.

                        4. Growing Community and Ecosystem

                          Dart has a growing community of developers and a vibrant ecosystem with libraries, frameworks, and tools. This provides support, resources, and opportunities for collaboration and knowledge sharing.