我在找出“错误状态:未来已经完成”的错误时遇到了麻烦。我基本上有一个使用BottomNavigationBar的应用程序,并且在我的一个屏幕上,我正在使用使用Completer类的此类。
再次创建Web视图时会发生这种情况:
body: WebView(
initialUrl: 'https://www.google.com/',
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
)
在第一个视图上一切正常,但是如果您选择另一个BottomNavigationBarItem并返回到该类,则将收到状态为Exception的异常:Future已经完成。下面是完整的代码。
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:url_launcher/url_launcher.dart';
import 'dart:async';
class myClass extends StatelessWidget {
Completer<WebViewController> _controller = Completer<WebViewController>();
final Set<String> _favorites = Set<String>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
NavigationControls(_controller.future),
Menu(_controller.future, () => _favorites),
],
),
//EXCEPTION OCCURS HERE
//BAD STATE: FUTURE ALREADY COMPLETED
body: WebView(
initialUrl: 'https://www.google.com/',
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
),
floatingActionButton: _bookmarkButton(),
);
}
_bookmarkButton() {
return FutureBuilder<WebViewController>(
future: _controller.future,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> controller) {
if (controller.hasData) {
return FloatingActionButton(
onPressed: () async {
var url = await controller.data.currentUrl();
_favorites.add(url);
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('Saved $url for later reading.')),
);
},
child: Icon(Icons.favorite),
);
}
return Container();
},
);
}
}
class Menu extends StatelessWidget {
Menu(this._webViewControllerFuture, this.favoritesAccessor);
final Future<WebViewController> _webViewControllerFuture;
final Function favoritesAccessor;
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _webViewControllerFuture,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> controller) {
if (!controller.hasData) return Container();
return PopupMenuButton<String>(
onSelected: (String value) async {
if (value == 'Email link') {
var url = await controller.data.currentUrl();
await launch(
'mailto:?subject=hello&body=$url');
} else {
var newUrl = await Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return FavoritesPage(favoritesAccessor());
}));
Scaffold.of(context).removeCurrentSnackBar();
if (newUrl != null) controller.data.loadUrl(newUrl);
}
},
itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
const PopupMenuItem<String>(
value: 'Email link',
child: Text('Email link'),
),
const PopupMenuItem<String>(
value: 'See Favorites',
child: Text('See Favorites'),
),
],
);
},
);
}
}
class FavoritesPage extends StatelessWidget {
FavoritesPage(this.favorites);
final Set<String> favorites;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Favorite pages')),
body: ListView(
children: favorites
.map((url) => ListTile(
title: Text(url), onTap: () => Navigator.pop(context, url)))
.toList()),
);
}
}
class NavigationControls extends StatelessWidget {
const NavigationControls(this._webViewControllerFuture)
: assert(_webViewControllerFuture != null);
final Future<WebViewController> _webViewControllerFuture;
@override
Widget build(BuildContext context) {
return FutureBuilder<WebViewController>(
future: _webViewControllerFuture,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> snapshot) {
final bool webViewReady =
snapshot.connectionState == ConnectionState.done;
final WebViewController controller = snapshot.data;
return Row(
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: !webViewReady
? null
: () => navigate(context, controller, goBack: true),
),
IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: !webViewReady
? null
: () => navigate(context, controller, goBack: false),
),
],
);
},
);
}
navigate(BuildContext context, WebViewController controller,
{bool goBack: false}) async {
bool canNavigate =
goBack ? await controller.canGoBack() : await controller.canGoForward();
if (canNavigate) {
goBack ? controller.goBack() : controller.goForward();
} else {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text("No ${goBack ? 'back' : 'forward'} history item")),
);
}
}
}
答案 0 :(得分:1)
试图重现该问题,但未能做到。您可以使用底部导航栏等显示代码吗?我是这样解释的,它在这里起作用:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: "/",
routes: {
"/": (context) => Home(),
},
);
}
}
class Home extends StatefulWidget {
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final navigatorKey = GlobalKey<NavigatorState>();
int _currentIndex = 0;
final pagesRouteFactories = {
'page1': (RouteSettings settings) => MaterialPageRoute<dynamic>(
builder: (BuildContext context) => HomePage(),
settings: settings,
),
'page2': (RouteSettings settings) => MaterialPageRoute<dynamic>(
builder: (BuildContext context) => MyClass(),
settings: settings,
),
};
@override
Widget build(BuildContext context) {
return Scaffold(
body: MaterialApp(
navigatorKey: navigatorKey,
onGenerateRoute: (RouteSettings route) =>
pagesRouteFactories[route.name](route),
initialRoute: 'page1',
),
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.white,
type: BottomNavigationBarType.fixed,
currentIndex: _currentIndex,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: FittedBox(
child: Text(
'Home',
style: TextStyle(fontWeight: FontWeight.w600),
),
),
),
BottomNavigationBarItem(
icon: Icon(Icons.web),
title: FittedBox(
child: Text(
'Web',
style: TextStyle(fontWeight: FontWeight.w600),
),
),
),
],
onTap: (int index) {
setState(() {
_currentIndex = index;
});
navigatorKey.currentState
.pushReplacementNamed(pagesRouteFactories.keys.toList()[index]);
},
),
);
}
}
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Text('hi'),
),
);
}
}
class MyClass extends StatelessWidget {
Completer<WebViewController> _controller = Completer<WebViewController>();
final Set<String> _favorites = Set<String>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
NavigationControls(_controller.future),
Menu(_controller.future, () => _favorites),
],
),
//EXCEPTION OCCURS HERE
//BAD STATE: FUTURE ALREADY COMPLETED
body: WebView(
initialUrl: 'https://www.google.com/',
onWebViewCreated: (WebViewController webViewController) {
_controller.complete(webViewController);
},
),
floatingActionButton: _bookmarkButton(),
);
}
_bookmarkButton() {
return FutureBuilder<WebViewController>(
future: _controller.future,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> controller) {
if (controller.hasData) {
return FloatingActionButton(
onPressed: () async {
var url = await controller.data.currentUrl();
_favorites.add(url);
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('Saved $url for later reading.')),
);
},
child: Icon(Icons.favorite),
);
}
return Container();
},
);
}
}
class Menu extends StatelessWidget {
Menu(this._webViewControllerFuture, this.favoritesAccessor);
final Future<WebViewController> _webViewControllerFuture;
final Function favoritesAccessor;
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _webViewControllerFuture,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> controller) {
if (!controller.hasData) return Container();
return PopupMenuButton<String>(
onSelected: (String value) async {
if (value == 'Email link') {
var url = await controller.data.currentUrl();
await launch('mailto:?subject=hello&body=$url');
} else {
var newUrl = await Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
return FavoritesPage(favoritesAccessor());
}));
Scaffold.of(context).removeCurrentSnackBar();
if (newUrl != null) controller.data.loadUrl(newUrl);
}
},
itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
const PopupMenuItem<String>(
value: 'Email link',
child: Text('Email link'),
),
const PopupMenuItem<String>(
value: 'See Favorites',
child: Text('See Favorites'),
),
],
);
},
);
}
}
class FavoritesPage extends StatelessWidget {
FavoritesPage(this.favorites);
final Set<String> favorites;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Favorite pages')),
body: ListView(
children: favorites
.map((url) => ListTile(
title: Text(url), onTap: () => Navigator.pop(context, url)))
.toList()),
);
}
}
class NavigationControls extends StatelessWidget {
const NavigationControls(this._webViewControllerFuture)
: assert(_webViewControllerFuture != null);
final Future<WebViewController> _webViewControllerFuture;
@override
Widget build(BuildContext context) {
return FutureBuilder<WebViewController>(
future: _webViewControllerFuture,
builder:
(BuildContext context, AsyncSnapshot<WebViewController> snapshot) {
final bool webViewReady =
snapshot.connectionState == ConnectionState.done;
final WebViewController controller = snapshot.data;
return Row(
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: !webViewReady
? null
: () => navigate(context, controller, goBack: true),
),
IconButton(
icon: const Icon(Icons.arrow_forward_ios),
onPressed: !webViewReady
? null
: () => navigate(context, controller, goBack: false),
),
],
);
},
);
}
navigate(BuildContext context, WebViewController controller,
{bool goBack: false}) async {
bool canNavigate =
goBack ? await controller.canGoBack() : await controller.canGoForward();
if (canNavigate) {
goBack ? controller.goBack() : controller.goForward();
} else {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text("No ${goBack ? 'back' : 'forward'} history item")),
);
}
}
}