Dart Completer() is another type of Future. With a Completer we can create our own Future.
If you are dealing with Flutter app and Dart, most of the time you don’t need to create your own Future. Cuz most of the flutter packages bring on their own Future.
If you create your own API, and if the API doesn’t return the response as Future, in that case we need to create and use our own Future using a Completer().
Let’s see a simple example first
import 'dart:async';
void main()async{
print(await myData());
print("new test data");
}
Future<int> myData(){
Completer<int> c = Completer<int>();
for(int i=0; i<100000000; i++){
if(i==847594){
c.complete(i);
print("ye");
break;
}
}
return c.future;
}
Let’s see an example
import 'dart:async';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:get/get.dart';
class WebViewPage extends StatefulWidget {
const WebViewPage({Key? key}) : super(key: key);
@override
_WebViewPageState createState() => _WebViewPageState();
}
class _WebViewPageState extends State<WebViewPage> {
late String selectedUrl;
double value = 0.0;
bool _canRedirect = true;
bool _isLoading = true;
final Completer<WebViewController> _controller = Completer<WebViewController>();
late WebViewController controllerGlobal;
@override
void initState() {
super.initState();
//selectedUrl = '${AppConstants.BASE_URL}/payment-mobile?customer_id=${widget.orderModel.userId}&order_id=${widget.orderModel.id}';
selectedUrl="https://learndart.net";
//if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => _exitApp(context),
child: Scaffold(
backgroundColor: Theme.of(context).primaryColor,
appBar: AppBar(
title: Text("Webview"),
leading: IconButton(icon:Icon(Icons.arrow_back_ios), onPressed: () { _exitApp(context); },),
),
body: Center(
child: Container(
width: double.maxFinite,
child: Stack(
children: [
WebView(
javascriptMode: JavascriptMode.unrestricted,
initialUrl: selectedUrl,
gestureNavigationEnabled: true,
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_3 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13E233 Safari/601.1',
onWebViewCreated: (WebViewController webViewController) {
_controller.future.then((value) => controllerGlobal = value);
_controller.complete(webViewController);
},
onPageStarted: (String url) {
print('Page started loading: $url');
setState(() {
_isLoading = true;
});
_redirect(url);
},
onPageFinished: (String url) {
print('Page finished loading: $url');
setState(() {
_isLoading = false;
});
_redirect(url);
},
),
_isLoading ? Center(
child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation<Color>(Theme.of(context).primaryColor)),
) : SizedBox.shrink(),
],
),
),
),
),
);
}
void _redirect(String url) {
if(_canRedirect) {
bool _isSuccess = url.contains('success') && url.contains("https://learndart.net");
bool _isFailed = url.contains('fail') && url.contains("https://learndart.net");
bool _isCancel = url.contains('cancel') && url.contains("https://learndart.net");
if (_isSuccess || _isFailed || _isCancel) {
_canRedirect = false;
}
if (_isSuccess) {
Get.to(()=>Container(child: Center(child:Text("Success")),));
} else if (_isFailed || _isCancel) {
Get.to(()=>Container(child: Center(child:Text("Failed")),));
}
}
}
Future<bool> _exitApp(BuildContext context) async {
if (await controllerGlobal.canGoBack()) {
controllerGlobal.goBack();
return Future.value(false);
} else {
Get.snackbar("Test", "Test webview");
return true;
}
}
}
This webview doesn’t return a Future. But we need to wait for the result to be returned and then use the callback function to redirect.
We can not use await and async here since, webview doesn’t return future response like that.
For this we need to use Completer() and wait for the response, once the response is received we can use the callback function _redirect.
Another example of dart completer
import 'dart:async';
import 'package:tutorial/models.dart';
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
final ThemeData _themeData = new ThemeData(
primaryColor: Colors.blue,
);
/// Root MaterialApp
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
var _routes = <String, WidgetBuilder>{
"/todos": (BuildContext context) => new TodosPage(),
// add another page,
};
return new MaterialApp(
title: "My App",
theme: _themeData,
home: new HomePage(),
routes: _routes,
);
}
}
/// place: "/"
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text("My Home Page")),
body: new RaisedButton(
child: new Text("My Todos"),
onPressed: _onPressed,
),
);
}
void _onPressed() {
Navigator.of(context).pushNamed("/todos");
}
}
/// place: "/todos"
class TodosPage extends StatefulWidget {
@override
_TodosPageState createState() => new _TodosPageState();
}
class _TodosPageState extends State<TodosPage> {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text("My Todos")),
body: new RefreshIndicator(
child: new ListView.builder(itemBuilder: _itemBuilder),
onRefresh: _onRefresh,
),
);
}
Future<Null> _onRefresh() {
Completer<Null> completer = new Completer<Null>();
Timer timer = new Timer(new Duration(seconds: 3), () {
completer.complete();
});
return completer.future;
}
Widget _itemBuilder(BuildContext context, int index) {
Todo todo = getTodo(index);
return new TodoItemWidget(todo: todo);
}
Todo getTodo(int index) {
return new Todo(false, "Todo $index");
}
}
class TodoItemWidget extends StatefulWidget {
TodoItemWidget({Key key, this.todo}) : super(key: key);
final Todo todo;
@override
_TodoItemWidgetState createState() => new _TodoItemWidgetState();
}
class _TodoItemWidgetState extends State<TodoItemWidget> {
@override
Widget build(BuildContext context) {
return new ListTile(
leading: new Text("-"),
title: new Text(widget.todo.name),
onTap: _onTap,
);
}
void _onTap() {
Route route = new MaterialPageRoute(
settings: new RouteSettings(name: "/todos/todo"),
builder: (BuildContext context) => new TodoPage(todo: widget.todo),
);
Navigator.of(context).push(route);
}
}
/// place: "/todos/todo"
class TodoPage extends StatefulWidget {
TodoPage({Key key, this.todo}) : super(key: key);
final Todo todo;
@override
_TodoPageState createState() => new _TodoPageState();
}
class _TodoPageState extends State<TodoPage> {
@override
Widget build(BuildContext context) {
var _children = <Widget>[
new Text("finished: " + widget.todo.finished.toString()),
new Text("name: " + widget.todo.name),
];
return new Scaffold(
appBar: new AppBar(title: new Text("My Todo")),
body: new Column(
children: _children,
),
);
}
}
class Todo{
bool finished;
String name;
Todo(this.finished, this.name);
}