© Fu Cheng 2019
F. ChengFlutter Recipeshttps://doi.org/10.1007/978-1-4842-4982-6_12

12. Platform Integration

Fu Cheng1 
(1)
Sandringham, Auckland, New Zealand
 

In mobile apps, it’s common to integrate with the native platform. You can write platform-specific code to use native platform API. There are a large number of plugins to perform different tasks.

12.1 Reading and Writing Files

Problem

You want to read and write files.

Solution

Use File API.

Discussion

In mobile apps, you may need to save files on the device. The dart:io library provides files API to read and write files. File class has methods to read content, write content, and query metadata of files. Operations with file system can be synchronous or asynchronous. Most of these operations have a pair of methods in File class. The asynchronous method returns a Future object, while the synchronous method uses Sync as the name suffix and returns the actual value. For example, readAsString() and readAsStringSync() methods are the pair for read operation that returns a string. Table 12-1 shows asynchronous methods of File class.
Table 12-1

Asynchronous methods of File

Name

Description

copy(String newPath)

Copy this file to a new path.

create({bool recursive: false})

Create this file. If recursive is true, all directories will be created.

open()

Open the file for random access with a RandomAccessFile object.

readAsBytes()

Read the entire file content as a list of bytes.

readAsString({Encoding encoding: utf8})

Read the entire file content as a string using specified encoding.

readAsLines(({Encoding encoding: utf8})

Read the entire file content as lines of text using specified encoding.

writeAsBytes(List<int> bytes)

Write a list of bytes to the file.

writeAsString(String contents)

Write a string to the file.

rename(String newPath)

Rename this file to a new path.

delete({bool recursive: false})

Delete this file.

exists()

Check whether this file exists.

stat()

Return a FileStat object that describes the file.

lastAccessed()

Get the last accessed time of this file.

lastModified()

Get the last modified time of this file.

length()

Get the length of this file.

Directory class represents directories in the file system. Given a Directory object, list() or listSync() methods can be used to list files and sub-directories.

To create File objects, you can use the default constructor with a path. For Flutter apps, the path may be platform-specific. There are two common places to store files for mobile apps:
  • Temporary directory to store temporary files that may be cleared at any time

  • Documents directory to store files that are private to the app and will only be cleared when the app is deleted

To get the platform-specific paths for these two locations, you can use the path_provider package ( https://pub.dev/packages/path_provider ). This package provides getTemporaryDirectory() function to get the path of the temporary directory and getApplicationDocumentsDirectory() function to get the application documents directory.

In Listing 12-1, readConfig() method reads the config.txt file from the application documents directory, while writeConfig() method writes a string to the same file.
class ConfigFile {
  Future<File> get _configFile async {
    Directory directory = await getApplicationDocumentsDirectory();
    return File('${directory.path}/config.txt');
  }
  Future<String> readConfig() async {
    return _configFile
        .then((file) => file.readAsString())
        .catchError((error) => 'default config');
  }
  Future<File> writeConfig(String config) async {
    File file = await _configFile;
    return file.writeAsString(config);
  }
}
Listing 12-1

Read and write files

12.2 Storing Key-Value Pairs

Problem

You want to store type-safe key-value pairs.

Solution

Use shared_preferences plugin .

Discussion

You can use files API to store any data on the device. Using generic files API means that you need to deal with data serialization and deserialization yourself. If the data you need to store is simple key-value pairs, using shared_preferences plugin ( https://pub.dev/packages/shared_preferences ) is a better choice. This plugin provides a map-based API to manage type-safe key-value pairs. The type of keys is always String. Only several types can be used as values, including String, bool, double, int, and List<String>.

To manage key-value pairs, you need to use the static SharedPreferences.getInstance() method to get the SharedPreferences object. Table 12-2 shows methods of SharedPreferences class. For each supported data type, there is a pair of methods to get and set the value. For example, getBool() and setBool() methods are used to get and set bool values.
Table 12-2

Methods of SharedPreference

Name

Description

get(String key)

Read the value for the specified key.

containsKey(String key)

Check whether specified key exists.

getKeys()

Get a set of keys.

remove(String key)

Remove the pair with the specified key.

clear()

Remove all pairs.

setString(String key, String value)

Write a String value.

getString()

Read a String value.

In Listing 12-2, SharedPreferences class is used to read and write a key-value pair.
class AppConfig {
  Future<SharedPreferences> _getPrefs() async {
    return await SharedPreferences.getInstance();
  }
  Future<String> getName() async {
    SharedPreferences prefs = await _getPrefs();
    return prefs.getString('name') ?? ";
  }
  Future<bool> setName(String name) async {
    SharedPreferences prefs = await _getPrefs();
    return prefs.setString('name', name);
  }
}
Listing 12-2

Use SharedPreferences

12.3 Writing Platform-Specific Code

Problem

You want to write platform-specific code.

Solution

Use platform channels to pass messages between Flutter app and the underlying host platform.

Discussion

In Flutter apps, most of code is written in platform agnostic Dart code. Features provided by Flutter SDK are limited. Sometimes you may still need to write platform-specific code to use native platform APIs. A generated Flutter app already has platform-specific code in android and ios directories. Code in these two directories is required to build native bundles.

Flutter uses message passing to call platform-specific APIs and get the result back. Messages are passed through platform channels. Flutter code sends messages to the host over a platform channel. Host code listens on the platform channel and receives the message. It then uses platform-specific API to generate the response and sends it back over the same channel to the Flutter code. Messages passed are actually asynchronous method calls.

In Flutter code, platform channels are created using MethodChannel class . All channel names in an app must be unique. It’s recommended to use a domain name as the prefix of channel names. To send method calls over a channel, these method calls must be encoded into binary format before being sent, and results received are decoded into Dart values. Encoding and decoding are done using subclasses of MethodCodec class :
  • StandardMethodCodec class uses standard binary encoding.

  • JSONMethodCodec class uses UTF-8 JSON encoding.

MethodChannel constructor has name parameter to specify the channel name and codec parameter to specify the MethodCodec object. The default MethodCodec object used is a StandardMethodCodec object.

Given a MethodChannel object, the invokeMethod() method invokes a method on the channel with specified arguments. The return value is a Future<T> object. This Future object may complete with different values:
  • It completes with the result if the method call succeeds.

  • It completes with a PlatformException if the method call fails.

  • It completes with a MissingPluginException if the method has not been implemented.

The invokeListMethod() method also invokes a method but returns a Future<List<T>> object. The invokeMapMethod() method invokes a method and returns a Future<Map<K, V>> object. Both invokeListMethod() and invokeMapMethod() methods use invokeMethod() internally, but add extra type cast.

In Listing 12-3, the getNetworkOperator method is invoked over the channel and returns the network operator.
class NetworkOperator extends StatefulWidget {
  @override
  _NetworkOperatorState createState() => _NetworkOperatorState();
}
class _NetworkOperatorState extends State<NetworkOperator> {
  static const channel = const MethodChannel('flutter-recipes/network');
  String _networkOperator = ";
  @override
  void initState() {
    super.initState();
    _getNetworkOperator();
  }
  Future<void> _getNetworkOperator() async {
    String operator;
    try {
      operator = await channel.invokeMethod('getNetworkOperator') ?? 'unknown';
    } catch (e) {
      operator = 'Failed to get network operator: ${e.message}';
    }
    setState(() {
      _networkOperator = operator;
    });
  }
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Center(
        child: Text(_networkOperator),
      ),
    );
  }
}
Listing 12-3

Get network operator

The handler of getNetworkOperator method call needs to be implemented in both Android and iOS platforms. Listing 12-4 shows the Java implementation. The getNetworkOperator() method uses Android API to get network operator. In the method call handler of the channel, if the method name is getNetworkOperator, the result of getNetworkOperator() method is sent back as success response using Result.success() method. If you want to send back error response, you can use Result.error() method. If the method is unknown, you should use Result.notImplemented() to mark the method as unimplemented.
public class MainActivity extends FlutterActivity {
  private static final String CHANNEL = "flutter-recipes/network";
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GeneratedPluginRegistrant.registerWith(this);
    new MethodChannel(getFlutterView(), CHANNEL)
        .setMethodCallHandler((methodCall, result) -> {
          if ("getNetworkOperator".equals(methodCall.method)) {
            result.success(getNetworkOperator());
          } else {
            result.notImplemented();
          }
        });
  }
  private String getNetworkOperator() {
    TelephonyManager telephonyManager =
        ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE));
    return telephonyManager.getNetworkOperatorName();
  }
}
Listing 12-4

Android implementation of getNetworkOperator

Listing 12-5 shows the AppDelegate.swift file for iOS platform. The receiveNetworkOperator() function uses iOS API to get the carrier name and send back as response using FlutterResult.
import UIKit
import Flutter
import CoreTelephony
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    guard let controller = window?.rootViewController as? FlutterViewController else {
      fatalError("rootViewController is not type FlutterViewController")
    }
    let networkChannel = FlutterMethodChannel(name: "flutter-recipes/network", binaryMessenger: controller)
    networkChannel.setMethodCallHandler({
      [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
      guard call.method == "getNetworkOperator" else {
        result(FlutterMethodNotImplemented)
        return
      }
      self?.receiveNetworkOperator(result: result)
    })
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
  private func receiveNetworkOperator(result: FlutterResult) {
    let networkInfo = CTTelephonyNetworkInfo()
    let carrier = networkInfo.subscriberCellularProvider
    result(carrier?.carrierName)
  }
}
Listing 12-5

Swift implementation of getNetworkOperator

12.4 Creating Plugins

Problem

You want to create sharable plugins that contain platform-specific code.

Solution

Create Flutter projects using the plugin template.

Discussion

Recipe 12-4 shows how to add platform-specific code to Flutter apps. Code added to a Flutter app cannot be shared between different apps. If you want to make the platform-specific code reusable, you can create Flutter plugins. Plugins are another type of projects supported in Flutter SDK. Plugins can be shared like other Dart packages using Dart pub tool ( https://pub.dev/ ).

To create a new Flutter plugin, you can use flutter create --template=plugin command. The template=plugin parameter means using the plugin template to create a Flutter project. You can choose to use either Java or Kotlin for Android and Objective-C or Swift for iOS, respectively. By default, Java is used for Android and Objective-C is used for iOS. You can use -a parameter with values java and kotlin to specify the language for Android and -i parameter with values objc and swift to specify the language for iOS. The following command shows how to create a plugin using Swift for iOS.
$ flutter create --template=plugin -i swift network

You can also use Android Studio or VS Code to create new plugins.

The newly created plugin already has skeleton code that gets the platform version. We can use the code in Recipe 12-3 to implement the plugin with new method to get the network operator. In the directory of generated plugin, there are several sub-directories:
  • The lib directory contains plugin’s public Dart API.

  • The android directory contains Android implementation of the public API.

  • The ios directory contains iOS implementation of the public API.

  • The example directory contains an example Flutter app that uses this plugin.

  • The test directory contains test code.

We first define the public Dart API in lib/network_plugin.dart file. In Listing 12-6, the value of the networkOperator property is retrieved by calling getNetworkOperator method using the method channel.
class NetworkPlugin {
  static const MethodChannel _channel =
    const MethodChannel('network_plugin');
  static Future<String> get networkOperator async {
    return await _channel.invokeMethod('getNetworkOperator');
  }
}
Listing 12-6

Plugin Dart API

The NetworkPlugin.java file in Listing 12-7 is the Android implementation of the plugin. NetworkPlugin class implements MethodCallHandler interface to handle method calls received from the platform channel.
public class NetworkPlugin implements MethodCallHandler {
  public static void registerWith(Registrar registrar) {
    final MethodChannel channel = new MethodChannel(registrar.messenger(), "network_plugin");
    channel.setMethodCallHandler(new NetworkPlugin(registrar));
  }
  NetworkPlugin(Registrar registrar) {
    this.registrar = registrar;
  }
  private final PluginRegistry.Registrar registrar;
  @Override
  public void onMethodCall(MethodCall call, Result result) {
    if (call.method.equals("getNetworkOperator")) {
      result.success(getNetworkOperator());
    } else {
      result.notImplemented();
    }
  }
  private String getNetworkOperator() {
    Context context = registrar.context();
    TelephonyManager telephonyManager =
        ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
    return telephonyManager.getNetworkOperatorName();
  }
}
Listing 12-7

Android implementation

The SwiftNetworkPlugin.swift file in Listing 12-8 is the Swift implementation of the plugin.
public class SwiftNetworkPlugin: NSObject, FlutterPlugin {
  public static func register(with registrar: FlutterPluginRegistrar) {
    let channel = FlutterMethodChannel(name: "network_plugin",
      binaryMessenger: registrar.messenger())
    let instance = SwiftNetworkPlugin()
    registrar.addMethodCallDelegate(instance, channel: channel)
  }
  public func handle(_ call: FlutterMethodCall,
      result: @escaping FlutterResult) {
    if (call.method == "getNetworkOperator") {
      self.receiveNetworkOperator(result: result)
    } else {
      result(FlutterMethodNotImplemented)
    }
  }
  private func receiveNetworkOperator(result: FlutterResult) {
    let networkInfo = CTTelephonyNetworkInfo()
    let carrier = networkInfo.subscriberCellularProvider
    result(carrier?.carrierName)
  }
}
Listing 12-8

Swift implementation

The example project and test code also need to be updated with new API.

12.5 Displaying Web Pages

Problem

You want to display web pages.

Solution

Use webview_flutter plugin.

Discussion

If you want to display web pages inside of Flutter apps, you can use webview_flutter plugin ( https://pub.dartlang.org/packages/webview_flutter ). After adding webview_flutter: ^0.3.6 to the dependencies of pubspec.yaml file, you can use WebView widget to show web pages and interact with them. For iOS, you need to add the io.flutter.embedded_views_preview key with value YES to the ios/Runner/Info.plist file.

Table 12-3 shows parameters of WebView constructor. To control the web view, you need to use onWebViewCreated callback to get the WebViewController object. The value of javascriptMode can be JavascriptMode.disabled or JavascriptMode.unrestricted. To enable JavaScript execution in the web pages, JavascriptMode.unrestricted should be set as the value. The navigationDelegate of type NavigationDelegate is a function that takes a NavigationRequest object and returns value of NavigationDecision enum. If the return value is NavigationDecision.prevent, the navigation request is blocked. If the return value is NavigationDecision.navigate, then navigation request can continue. You can use navigation delegate to block users from accessing restricted pages. The onPageFinished callback receives the URL of the loaded page.
Table 12-3

Parameters of WebView constructor

Name

Description

initialUrl

The initial URL to load.

onWebViewCreated

Callback when the WebView is created.

javascriptMode

Whether JavaScript is enabled.

javascriptChannels

Channels to receive messages sent by JavaScript code running in the web view.

navigationDelegate

Determines whether a navigation request should be handled.

onPageFinished

Callback when a page loading is finished.

gestureRecognizers

Gestures recognized by the web view.

After getting the WebViewController object, you can use methods shown in Table 12-4 to interact with the web view. All these methods are asynchronous and return Future objects. For example, the canGoBack() method returns a Future<bool> object.
Table 12-4

Methods of WebViewController

Name

Description

evaluateJavascript(String javascriptString)

Evaluate JavaScript code in the context of current page.

loadUrl(String url, { Map<String, String> headers }

Load the specified URL.

reload()

Reload the current URL.

goBack()

Go back in the navigation history.

canGoBack()

Whether it’s valid to go back in the history.

goForward()

Go forward in the navigation history.

canGoForward()

Whether it’s valid to go forward in history.

clearCache()

Clear the cache.

currentUrl()

Get the current URL.

Listing 12-9 shows an example of using WebView widget to interact with Google Search page. Because the creation of WebView widget is asynchronous, the Completer<WebViewController> object is used to capture the WebViewController object. In the onWebViewCreated callback, the Completer<WebViewController> object is completed with the created WebViewController object . In the onPageFinished callback, the evaluateJavascript() method of WebViewController object is used to execute JavaScript code that sets value to the input and clicks the search button. This causes the WebView widget to load the search result page.

The JavascriptChannel object is created with a channel name and a JavascriptMessageHandler function to handle the messages sent from JavaScript code running in the web page. The message handler in Listing 12-9 uses a SnackBar widget to show the received message. The channel name “Messenger” becomes the global object that has a postMessage function to be used in JavaScript code to send messages back.
class GoogleSearch extends StatefulWidget {
  @override
  _GoogleSearchState createState() => _GoogleSearchState();
}
class _GoogleSearchState extends State<GoogleSearch> {
  final Completer<WebViewController> _controller =
      Completer<WebViewController>();
  @override
  Widget build(BuildContext context) {
    return WebView(
      initialUrl: 'https://google.com',
      javascriptMode: JavascriptMode.unrestricted,
      javascriptChannels:
          <JavascriptChannel>[_javascriptChannel(context)].toSet(),
      onWebViewCreated: (WebViewController webViewController) {
        _controller.complete(webViewController);
      },
      onPageFinished: (String url) {
        _controller.future.then((WebViewController webViewController) {
          webViewController.evaluateJavascript(
              'Messenger.postMessage("Loaded in " + navigator.userAgent);');
          webViewController.evaluateJavascript(
              'document.getElementsByName("q")[0].value="flutter";'
              'document.querySelector("button[aria-label*=Search]").click();');
        }) ;
      },
    );
  }
  JavascriptChannel _javascriptChannel(BuildContext context) {
    return JavascriptChannel(
        name: 'Messenger',
        onMessageReceived: (JavascriptMessage message) {
          Scaffold.of(context).showSnackBar(
            SnackBar(content: Text(message.message)),
          );
        });
  }
}
Listing 12-9

Use WebView

12.6 Playing Videos

Problem

You want to play videos.

Solution

Use video_player plugin .

Discussion

If you want play videos from assets, file system, or network, you can use video_player plugin ( https://pub.dev/packages?q=video_player ). To use this plugin, you need to add video_player: ^0.10.0+5 to the dependencies of pubspec.yaml file. For iOS, you need to use a real device instead of a simulator for development and testing. If you want to load videos from arbitrary locations, you need to add the code in Listing 12-10 to ios/Runner/Info.plist file. Using NSAllowsArbitraryLoads reduces the security of the app. It’s better to check Apple’s guide ( https://developer.apple.com/documentation/security/preventing_insecure_network_connections ) for network security.
<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>
Listing 12-10

iOS HTTP security config

If you need to load videos from network on Android, you need to add code in Listing 12-11 to the android/app/src/main /AndroidManifest.xml file.
<uses-permission android:name="android.permission.INTERNET"/>
Listing 12-11

Android

To play videos, you need to use constructors shown in Table 12-5 to create VideoPlayerController objects.
Table 12-5

Constructors of VideoPlayerController

Name

Description

VideoPlayerController.asset(String dataSource, { String package })

Play a video from assets.

VideoPlayerController.file(File file)

Play a video from local file system.

VideoPlayerController.network(String dataSource)

Play a video loaded from network.

After creating a VideoPlayerController object, you can use methods shown in Table 12-6 to control the video playing. All these methods return Future objects. The initialize() method must be called first to initialize the controller. You can only call other methods after the Future object returned by initialize() method completes successfully.
Table 12-6

Methods of VideoPlayerController

Name

Description

play()

Play the video.

pause()

Pause the video.

seekTo(Duration moment)

Seek to the specified position.

setLooping(bool looping)

Whether to loop the video.

setVolume(double volume)

Set the volume of audio.

initialize()

Initialize the controller.

dispose()

Dispose the controller and clean up resources.

VideoPlayerController class extends from ValueNotifier<VideoPlayerValue> class. You can get notified when the state changes by adding listeners to it. VideoPlayerValue class contains different properties to access the state of the video. VideoPlayer class is the actual widget that displays the video. It requires a VideoPlayerController object.

VideoPlayerView class in Listing 12-12 is a widget to play video loaded from specified URL. In the initState() method, VideoPlayerController.network() constructor is used to create the VideoPlayerController object. FutureBuilder widget uses the Future object returned by initialize() method to build the UI. Since VideoPlayerController object is also a Listenable object, we can use AnimatedBuilder with the VideoPlayerController object. AspectRatio widget uses the aspectRatio property to make sure the proper aspect ratio is used when playing the video. VideoProgressIndicator widget shows a progress bar to indicate video playback progress.
class VideoPlayerView extends StatefulWidget {
  VideoPlayerView({Key key, this.videoUrl}) : super(key: key);
  final String videoUrl;
  @override
  _VideoPlayerViewState createState() => _VideoPlayerViewState();
}
class _VideoPlayerViewState extends State<VideoPlayerView> {
  VideoPlayerController _controller;
  Future<void> _initializedFuture;
  @override
  void initState() {
    super.initState();
    _controller = VideoPlayerController.network(widget.videoUrl);
    _initializedFuture = _controller.initialize();
  }
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: _initializedFuture,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          return AnimatedBuilder(
            animation: _controller,
            child: VideoProgressIndicator(_controller, allowScrubbing: true),
            builder: (context, child) {
              return Column(
                children: <Widget>[
                  AspectRatio(
                    aspectRatio: _controller.value.aspectRatio,
                    child: VideoPlayer(_controller),
                  ),
                  Row(
                    children: <Widget>[
                      IconButton(
                        icon: Icon(_controller.value.isPlaying
                            ? Icons.pause
                            : Icons.play_arrow),
                        onPressed: () {
                          if (_controller.value.isPlaying) {
                            _controller.pause();
                          } else {
                            _controller.play();
                          }
                        },
                      ),
                      Expanded(child: child),
                    ],
                  ),
                ],
              );
            },
          );
        } else {
          return Center(child: CircularProgressIndicator());
        }
      },
    );
  }
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}
Listing 12-12

Playing video

12.7 Using Cameras

Problem

You want to use cameras to take pictures or record videos.

Solution

Use camera plugin.

Discussion

If you want to access the cameras on the device, you can use camera plugin ( https://pub.dev/packages/camera ). To install this plugin, you need to add camera: ^0.5.0 to the dependencies of pubspec.yaml file. For iOS, you need to add code in Listing 12-13 to the ios/Runner/Info.plist file. These two key-value pairs describe the purpose of accessing camera and microphone. This is required to protect user privacy.
<key>NSCameraUsageDescription</key>
<string>APPNAME requires access to your phone's camera.</string>
<key>NSMicrophoneUsageDescription</key>
<string>APPNAME requires access to your phone's microphone.</string>
Listing 12-13

Privacy requirements for iOS

For Android, the minimum Android SDK version needs to set to 21 in the android/app/build.gradle file.

To access cameras, you need to create CameraController objects. CameraController constructor requires parameters of types CameraDescription and ResolutionPreset. CameraDescription class describes a camera. ResolutionPreset enum describes the quality of screen resolution. ResolutionPreset is an enum with values low, medium, and high. To get CameraDescription objects, you can use availableCameras() function to get a list of available cameras with type List<CameraDescription>.

Table 12-7 shows methods of CameraController class. All these methods return Future objects. A CameraController object needs to be initialized first. Other methods should only be called after the Future object returned by initialize() completes successfully. CameraController class extends from ValueNotifier<CameraValue> class, so you can add listeners to it to get notified of state changes.
Table 12-7

Methods of CameraController

Name

Description

takePicture(String path)

Take a picture and save to a file.

prepareForVideoRecording()

Prepare for video recording.

startVideoRecording(String filePath)

Start a video recording and save to a file.

stopVideoRecording()

Stop the current video recording.

startImageStream()

Start streaming of images.

stopImageStream()

Stop the current streaming of images.

initialize()

Initialize the controller.

dispose()

Dispose the controller and clean up resources.

In Listing 12-14, the CameraController object is created with passed-in CameraDescription object. FutureBuilder widget builds the actual UI after the CameraController object is initialized. CameraPreview widget shows live preview of the camera. When the icon is pressed, a picture is taken and saved to the temporary directory.
class CameraView extends StatefulWidget {
  CameraView({Key key, this.camera}) : super(key: key);
  final CameraDescription camera;
  @override
  _CameraViewState createState() => _CameraViewState();
}
class _CameraViewState extends State<CameraView> {
  CameraController _controller;
  Future<void> _initializedFuture;
  @override
  void initState() {
    super.initState();
    _controller = CameraController(widget.camera, ResolutionPreset.high);
    _initializedFuture = _controller.initialize();
  }
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<void>(
      future: _initializedFuture,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          return Column(
            children: <Widget>[
              Expanded(child: CameraPreview(_controller)),
              IconButton(
                icon: Icon(Icons.photo_camera),
                onPressed: () async {
                  String path = join((await getTemporaryDirectory()).path,
                      '${DateTime.now()}.png');
                  await _controller.takePicture(path);
                  Scaffold.of(context).showSnackBar(
                      SnackBar(content: Text('Picture saved to $path')));
                },
              ),
            ],
          );
        } else {
          return Center(child: CircularProgressIndicator());
        }
      },
    );
  }
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}
Listing 12-14

Use camera

In Listing 12-15, availableCameras() function gets a list of CameraDescription objects and only the first one is used to create the CameraView widget.
class CameraSelector extends StatelessWidget {
  final Future<CameraDescription> _cameraFuture =
      availableCameras().then((list) => list.first);
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<CameraDescription>(
      future: _cameraFuture,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
          if (snapshot.hasData) {
            return CameraView(camera: snapshot.data);
          } else {
            return Center(child: Text('No camera available!'));
          }
        } else {
          return Center(child: CircularProgressIndicator());
        }
      },
    );
  }
}
Listing 12-15

Select camera

12.8 Using System Share Sheet

Problem

You want to allow user sharing items using system share sheet.

Solution

Use share plugin.

Discussion

If you want to allow user sharing items in the app, you can use the share plugin ( https://pub.dev/packages/share ) to show the system share sheet. To use this plugin, you need to add share: ^0.6.1 to the dependencies of pubspec.yaml file.

The API provided by share plugin is very simple. It only has a static share() method to share some text. You can share plain text or a URL. Listing 12-16 shows how to use share() method to share a URL.
Share.share('https://flutter.dev');
Listing 12-16

Share a URL

12.9 Summary

Flutter apps can use platform-specific code to call native platform APIs. There are a large number of community plugins to use different futures on the native platform, including cameras, microphones, sensors, and more. In the next chapter, we’ll discuss miscellaneous topics in Flutter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.161.116