每次点击底部导航栏的按钮时都会重新加载 WebView
Reload WebView every time when hitting buttons at bottom navbar in flutter
我正在使用 Flutter Webview 构建我的第一个网络应用程序,我在其中嵌入了一个底部导航栏,该导航栏还有 4 个图标。
每个图标都有自己的 Class,按下它会为每个选项卡启动不同的 Webview,如下例所示:
但是我在上面面临的问题是您可以看到每个选项卡不能一次按下多次。我正在寻找的是像 html 中的超链接一样重新打开同一个选项卡,而不是只打开一次。
此外,无论我在哪个页面或选项卡上,我都不知道如何在点击刷新图标时刷新 webview 页面
main.dart
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:splash_screen_view/SplashScreenView.dart';
import 'pages/home_page.dart';
import 'pages/profile.dart';
import 'pages/cart.dart';
void main(){
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Color(0xff1e2229)
));
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
Widget spalshfirst = SplashScreenView(
navigateRoute: WebViewClass(),
duration: 3000,
imageSize: 80,
imageSrc: 'assets/splash.png',
text: "Food Delivery",
textType: TextType.TyperAnimatedText,
textStyle: TextStyle(
fontSize: 25.0,
),
colors: const [
Colors.purple,
Colors.blue,
Colors.yellow,
Colors.red,
],
backgroundColor: Colors.white,
);
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: spalshfirst
)
);
}
}
class WebViewClass extends StatefulWidget {
WebViewState createState() => WebViewState();
}
class WebViewState extends State<WebViewClass> with TickerProviderStateMixin{
@override
void initState() {
super.initState();
// Enable hybrid composition.
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
}
int currentIndex = 0;
@override
Widget build(BuildContext context) {
//Including Dart Webview pages
final screens = [
HomeClass(),
Center(child: Text('refresh')),
ProfileClass(),
CartClass()
];
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: null,
body: SafeArea(
child: screens[currentIndex]
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentIndex,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.white60,
onTap: (index) => setState(() => currentIndex = index),
items: const [
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.home),
label: 'Home',
backgroundColor: Colors.pink
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.refresh),
label: 'Refresh',
backgroundColor: Colors.pink
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.profile_circled),
label: 'Profile',
backgroundColor: Colors.pink
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.cart),
label: 'Cart',
backgroundColor: Colors.pink
)
],
) ,
);
}
}
Webview 主页Class() 引用
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class HomeClass extends StatefulWidget {
Homestate createState() => Homestate();
}
class Homestate extends State<HomeClass> with TickerProviderStateMixin{
late WebViewController _controller;
final Completer<WebViewController> _controllerCompleter = Completer<WebViewController>();
//Make sure this function return Future<bool> otherwise you will get an error
Future<bool> _onWillPop(BuildContext context) async {
if (await _controller.canGoBack()) {
_controller.goBack();
return Future.value(false);
} else {
return Future.value(true);
}
}
bool isLoading = false;
final key = UniqueKey();
int position = 0;
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => _goBack(context),
child: Scaffold(
appBar: null,
body: IndexedStack(
index: position,
children: [
WebView(
initialUrl: 'https://google.com',
javascriptMode: JavascriptMode.unrestricted,
key: key,
onPageStarted: (value) {
setState(() {
position = 1;
});
},
onPageFinished: (value) {
setState(() {
position = 0;
});
},
onWebViewCreated: (WebViewController webViewController) {
_controllerCompleter.future.then((value) => _controller = value);
_controllerCompleter.complete(webViewController);
},
),
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.white.withOpacity(0.5),
child: Center(
child: SpinKitDualRing(
color: Colors.pinkAccent,
size: 45.0,
controller: AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1200),
),
),
),
)
],
))
);
}
//Go back coding
Future<bool> _goBack(BuildContext context) async {
if (await _controller.canGoBack()) {
_controller.goBack();
return Future.value(false);
} else {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Do you want to exit from Foodrive?'),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('No'),
),
TextButton(
onPressed: () {
SystemNavigator.pop();
},
child: const Text('Yes'),
),
],
));
return Future.value(true);
}
}
}
我为这个答案付钱给某人后找到了解决方案。在此解决方案中,开发人员添加了 2 个新包 import 'package:get/get.dart';
和 import 'package:testing/pages/navigation_controller.dart';
依赖项
dependencies:
flutter:
sdk: flutter
webview_flutter: ^2.3.0
flutter_spinkit: ^5.1.0
splash_screen_view: ^3.0.0
flutter_icons: ^1.1.0
pull_to_refresh: ^2.0.0
get:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.4
main.dart
// ignore_for_file: prefer_const_constructors
// ignore: use_key_in_widget_constructors
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:testing/pages/navigation_controller.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:splash_screen_view/SplashScreenView.dart';
import 'initial_bindings.dart';
import 'pages/home_page.dart';
import 'pages/profile.dart';
import 'pages/cart.dart';
void main() {
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(statusBarColor: Color(0xff1e2229)));
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
Widget spalshfirst = SplashScreenView(
navigateRoute: WebViewClass(),
duration: 3000,
imageSize: 80,
imageSrc: 'assets/splash.png',
text: "Food Delivery",
textType: TextType.TyperAnimatedText,
textStyle: TextStyle(
fontSize: 25.0,
),
colors: const [
Colors.purple,
Colors.blue,
Colors.yellow,
Colors.red,
],
backgroundColor: Colors.white,
);
return GetMaterialApp(
initialBinding: InitialBindings(),
debugShowCheckedModeBanner: false,
home: Scaffold(body: spalshfirst));
}
}
class WebViewClass extends StatefulWidget {
WebViewState createState() => WebViewState();
}
class WebViewState extends State<WebViewClass> with TickerProviderStateMixin {
@override
void initState() {
super.initState();
// Enable hybrid composition.
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
}
int currentIndex = 0;
@override
Widget build(BuildContext context) {
//Including Dart Webview pages
final screens = [
HomeClass(),
// Center(child: Text('refresh')),
ProfileClass(),
CartClass()
];
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: null,
body: SafeArea(child: screens[currentIndex]),
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.pink,
currentIndex: currentIndex,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.white60,
onTap: (index) {
setState(() => currentIndex = index);
Get.find<NavigationController>().controller.value?.reload();
},
items: const [
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.home),
label: 'Home',
backgroundColor: Colors.pink),
// BottomNavigationBarItem(
// icon: Icon(CupertinoIcons.refresh),
// label: 'Refresh',
// backgroundColor: Colors.pink),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.profile_circled),
label: 'Profile',
backgroundColor: Colors.red),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.cart),
label: 'Cart',
backgroundColor: Colors.pink)
],
),
);
}
}
initial_bindings.dart
import 'package:get/get.dart';
import 'package:webview_flutter/webview_flutter.dart';
class NavigationController extends GetxController {
Rx<WebViewController?> controller = null.obs;
}
Home_page.dart 相同的代码(profile.dart 和 cart.dart)
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:testing/pages/navigation_controller.dart';
import 'package:webview_flutter/webview_flutter.dart';
class HomeClass extends StatefulWidget {
Homestate createState() => Homestate();
}
class Homestate extends State<HomeClass> with TickerProviderStateMixin {
late WebViewController _controller;
final Completer<WebViewController> _controllerCompleter =
Completer<WebViewController>();
final RefreshController _refreshController =
RefreshController(initialRefresh: false);
late AnimationController animController;
@override
void initState() {
super.initState();
animController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1200),
);
}
@override
void dispose() {
animController.dispose();
super.dispose();
}
//Make sure this function return Future<bool> otherwise you will get an error
Future<bool> _onWillPop(BuildContext context) async {
if (await _controller.canGoBack()) {
_controller.goBack();
return Future.value(false);
} else {
return Future.value(true);
}
}
bool isLoading = false;
final key = UniqueKey();
int position = 0;
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => _goBack(context),
child: Scaffold(
appBar: null,
body: SmartRefresher(
controller: _refreshController,
enablePullDown: true,
onRefresh: () {
Get.find<NavigationController>().controller.value?.reload();
_refreshController.refreshCompleted();
},
child: IndexedStack(
index: position,
children: [
WebView(
initialUrl: 'https://canada.ca',
javascriptMode: JavascriptMode.unrestricted,
key: key,
onPageStarted: (value) {
setState(() {
position = 1;
});
},
onPageFinished: (value) {
setState(() {
position = 0;
});
},
onWebViewCreated: (WebViewController webViewController) {
_controllerCompleter.future
.then((value) => _controller = value);
_controllerCompleter.complete(webViewController);
Get.find<NavigationController>().controller =
webViewController.obs;
},
),
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.white.withOpacity(0.5),
child: Center(
child: SpinKitDualRing(
color: Colors.pinkAccent,
size: 45.0,
controller: animController,
),
),
)
],
),
),
),
);
}
//Go back coding
Future<bool> _goBack(BuildContext context) async {
if (await _controller.canGoBack()) {
_controller.goBack();
return Future.value(false);
} else {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Do you want to exit from Foodrive?'),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('No'),
),
TextButton(
onPressed: () {
SystemNavigator.pop();
},
child: const Text('Yes'),
),
],
));
return Future.value(true);
}
}
}
我正在使用 Flutter Webview 构建我的第一个网络应用程序,我在其中嵌入了一个底部导航栏,该导航栏还有 4 个图标。
每个图标都有自己的 Class,按下它会为每个选项卡启动不同的 Webview,如下例所示:
但是我在上面面临的问题是您可以看到每个选项卡不能一次按下多次。我正在寻找的是像 html 中的超链接一样重新打开同一个选项卡,而不是只打开一次。
此外,无论我在哪个页面或选项卡上,我都不知道如何在点击刷新图标时刷新 webview 页面
main.dart
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:splash_screen_view/SplashScreenView.dart';
import 'pages/home_page.dart';
import 'pages/profile.dart';
import 'pages/cart.dart';
void main(){
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Color(0xff1e2229)
));
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
Widget spalshfirst = SplashScreenView(
navigateRoute: WebViewClass(),
duration: 3000,
imageSize: 80,
imageSrc: 'assets/splash.png',
text: "Food Delivery",
textType: TextType.TyperAnimatedText,
textStyle: TextStyle(
fontSize: 25.0,
),
colors: const [
Colors.purple,
Colors.blue,
Colors.yellow,
Colors.red,
],
backgroundColor: Colors.white,
);
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: spalshfirst
)
);
}
}
class WebViewClass extends StatefulWidget {
WebViewState createState() => WebViewState();
}
class WebViewState extends State<WebViewClass> with TickerProviderStateMixin{
@override
void initState() {
super.initState();
// Enable hybrid composition.
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
}
int currentIndex = 0;
@override
Widget build(BuildContext context) {
//Including Dart Webview pages
final screens = [
HomeClass(),
Center(child: Text('refresh')),
ProfileClass(),
CartClass()
];
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: null,
body: SafeArea(
child: screens[currentIndex]
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentIndex,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.white60,
onTap: (index) => setState(() => currentIndex = index),
items: const [
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.home),
label: 'Home',
backgroundColor: Colors.pink
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.refresh),
label: 'Refresh',
backgroundColor: Colors.pink
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.profile_circled),
label: 'Profile',
backgroundColor: Colors.pink
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.cart),
label: 'Cart',
backgroundColor: Colors.pink
)
],
) ,
);
}
}
Webview 主页Class() 引用
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class HomeClass extends StatefulWidget {
Homestate createState() => Homestate();
}
class Homestate extends State<HomeClass> with TickerProviderStateMixin{
late WebViewController _controller;
final Completer<WebViewController> _controllerCompleter = Completer<WebViewController>();
//Make sure this function return Future<bool> otherwise you will get an error
Future<bool> _onWillPop(BuildContext context) async {
if (await _controller.canGoBack()) {
_controller.goBack();
return Future.value(false);
} else {
return Future.value(true);
}
}
bool isLoading = false;
final key = UniqueKey();
int position = 0;
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => _goBack(context),
child: Scaffold(
appBar: null,
body: IndexedStack(
index: position,
children: [
WebView(
initialUrl: 'https://google.com',
javascriptMode: JavascriptMode.unrestricted,
key: key,
onPageStarted: (value) {
setState(() {
position = 1;
});
},
onPageFinished: (value) {
setState(() {
position = 0;
});
},
onWebViewCreated: (WebViewController webViewController) {
_controllerCompleter.future.then((value) => _controller = value);
_controllerCompleter.complete(webViewController);
},
),
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.white.withOpacity(0.5),
child: Center(
child: SpinKitDualRing(
color: Colors.pinkAccent,
size: 45.0,
controller: AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1200),
),
),
),
)
],
))
);
}
//Go back coding
Future<bool> _goBack(BuildContext context) async {
if (await _controller.canGoBack()) {
_controller.goBack();
return Future.value(false);
} else {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Do you want to exit from Foodrive?'),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('No'),
),
TextButton(
onPressed: () {
SystemNavigator.pop();
},
child: const Text('Yes'),
),
],
));
return Future.value(true);
}
}
}
我为这个答案付钱给某人后找到了解决方案。在此解决方案中,开发人员添加了 2 个新包 import 'package:get/get.dart';
和 import 'package:testing/pages/navigation_controller.dart';
依赖项
dependencies:
flutter:
sdk: flutter
webview_flutter: ^2.3.0
flutter_spinkit: ^5.1.0
splash_screen_view: ^3.0.0
flutter_icons: ^1.1.0
pull_to_refresh: ^2.0.0
get:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.4
main.dart
// ignore_for_file: prefer_const_constructors
// ignore: use_key_in_widget_constructors
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:testing/pages/navigation_controller.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:splash_screen_view/SplashScreenView.dart';
import 'initial_bindings.dart';
import 'pages/home_page.dart';
import 'pages/profile.dart';
import 'pages/cart.dart';
void main() {
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(statusBarColor: Color(0xff1e2229)));
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
Widget spalshfirst = SplashScreenView(
navigateRoute: WebViewClass(),
duration: 3000,
imageSize: 80,
imageSrc: 'assets/splash.png',
text: "Food Delivery",
textType: TextType.TyperAnimatedText,
textStyle: TextStyle(
fontSize: 25.0,
),
colors: const [
Colors.purple,
Colors.blue,
Colors.yellow,
Colors.red,
],
backgroundColor: Colors.white,
);
return GetMaterialApp(
initialBinding: InitialBindings(),
debugShowCheckedModeBanner: false,
home: Scaffold(body: spalshfirst));
}
}
class WebViewClass extends StatefulWidget {
WebViewState createState() => WebViewState();
}
class WebViewState extends State<WebViewClass> with TickerProviderStateMixin {
@override
void initState() {
super.initState();
// Enable hybrid composition.
if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
}
int currentIndex = 0;
@override
Widget build(BuildContext context) {
//Including Dart Webview pages
final screens = [
HomeClass(),
// Center(child: Text('refresh')),
ProfileClass(),
CartClass()
];
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: null,
body: SafeArea(child: screens[currentIndex]),
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.pink,
currentIndex: currentIndex,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.white60,
onTap: (index) {
setState(() => currentIndex = index);
Get.find<NavigationController>().controller.value?.reload();
},
items: const [
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.home),
label: 'Home',
backgroundColor: Colors.pink),
// BottomNavigationBarItem(
// icon: Icon(CupertinoIcons.refresh),
// label: 'Refresh',
// backgroundColor: Colors.pink),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.profile_circled),
label: 'Profile',
backgroundColor: Colors.red),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.cart),
label: 'Cart',
backgroundColor: Colors.pink)
],
),
);
}
}
initial_bindings.dart
import 'package:get/get.dart';
import 'package:webview_flutter/webview_flutter.dart';
class NavigationController extends GetxController {
Rx<WebViewController?> controller = null.obs;
}
Home_page.dart 相同的代码(profile.dart 和 cart.dart)
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:testing/pages/navigation_controller.dart';
import 'package:webview_flutter/webview_flutter.dart';
class HomeClass extends StatefulWidget {
Homestate createState() => Homestate();
}
class Homestate extends State<HomeClass> with TickerProviderStateMixin {
late WebViewController _controller;
final Completer<WebViewController> _controllerCompleter =
Completer<WebViewController>();
final RefreshController _refreshController =
RefreshController(initialRefresh: false);
late AnimationController animController;
@override
void initState() {
super.initState();
animController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1200),
);
}
@override
void dispose() {
animController.dispose();
super.dispose();
}
//Make sure this function return Future<bool> otherwise you will get an error
Future<bool> _onWillPop(BuildContext context) async {
if (await _controller.canGoBack()) {
_controller.goBack();
return Future.value(false);
} else {
return Future.value(true);
}
}
bool isLoading = false;
final key = UniqueKey();
int position = 0;
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => _goBack(context),
child: Scaffold(
appBar: null,
body: SmartRefresher(
controller: _refreshController,
enablePullDown: true,
onRefresh: () {
Get.find<NavigationController>().controller.value?.reload();
_refreshController.refreshCompleted();
},
child: IndexedStack(
index: position,
children: [
WebView(
initialUrl: 'https://canada.ca',
javascriptMode: JavascriptMode.unrestricted,
key: key,
onPageStarted: (value) {
setState(() {
position = 1;
});
},
onPageFinished: (value) {
setState(() {
position = 0;
});
},
onWebViewCreated: (WebViewController webViewController) {
_controllerCompleter.future
.then((value) => _controller = value);
_controllerCompleter.complete(webViewController);
Get.find<NavigationController>().controller =
webViewController.obs;
},
),
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.white.withOpacity(0.5),
child: Center(
child: SpinKitDualRing(
color: Colors.pinkAccent,
size: 45.0,
controller: animController,
),
),
)
],
),
),
),
);
}
//Go back coding
Future<bool> _goBack(BuildContext context) async {
if (await _controller.canGoBack()) {
_controller.goBack();
return Future.value(false);
} else {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Do you want to exit from Foodrive?'),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('No'),
),
TextButton(
onPressed: () {
SystemNavigator.pop();
},
child: const Text('Yes'),
),
],
));
return Future.value(true);
}
}
}