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
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.
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.
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>.
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. |
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.
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.
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.
Get network operator
Android implementation of getNetworkOperator
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/ ).
You can also use Android Studio or VS Code to create new plugins.
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.
Plugin Dart API
Android implementation
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.
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. |
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.
Use WebView
12.6 Playing Videos
Problem
You want to play videos.
Solution
Use video_player plugin .
Discussion
iOS HTTP security config
Android
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. |
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.
Playing video
12.7 Using Cameras
Problem
You want to use cameras to take pictures or record videos.
Solution
Use camera plugin.
Discussion
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>.
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. |
Use camera
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.
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.