如何从 PageView class flutter 关闭屏幕
How to close a screen from PageView class flutter
您好,
我有一个非常具体的问题要问。我必须用步骤和图片来解释它,所以它们就在那里。
我有一个包含三个屏幕的应用程序:
主提要屏幕,
主聊天和请求屏幕,
主配置文件屏幕,
它们都是 PageView 的一部分。此 PageView class 在名为 main_tab_controller.dart 的 class 中进行控制。在 class 中,在 initState() 中,我有一个 Firebase 消息传递方法,每次收到通知 (onMessage) 时都会调用该方法。因此,每次我收到此通知时,我都会显示一个如下所示的叠加层。
并且它在这三个主屏幕上完美运行。如果是聊天通知,我会将 PageView 定向到第二个屏幕,即 MainChatAndRequest 屏幕,然后打开聊天屏幕。如果是请求通知,我会将 PageView 定向到第二个屏幕,即 MainChatAndRequest 屏幕,然后打开请求屏幕。
但我遇到的问题如下。在我的 MainFeedScreen 和 MainProfileScreen 中,我打开了一些其他屏幕。例如,在 MainFeedScreen 中,我打开 UserDetailsScreen 或 FilterScreen。或者在 MainProfileScreen 中,我打开 SettingsScreen 或 EditUserProfileScreen。
所以我的问题是:例如,如果我导航到 MainProfileScreen 并在该屏幕中打开 SettingsScreen,并且我收到叠加顶部消息,我如何关闭当前打开的 SettingsScreen 并导航回第二个屏幕即,main_tab_controller.dart 的 initState() 中的 Firebase 消息传递函数的 MainChatsAndRequestsScreen 是所有其他屏幕的 parent。
您有下图:
我什么都试过了,Navigator.popUntil(context),Navigator.pushReplacement(context),用过Navigator.pushNamed(context),但都没有用。如果有人能帮助我,我将不胜感激。
只是为了让您更好地理解屏幕:
Parent 屏幕是具有三个屏幕的 PageView:
- 主提要屏幕
- 主聊天和请求屏幕
- 主配置文件屏幕
然后在主提要屏幕中您有:
- 过滤屏幕
- 个人资料详细信息屏幕
在主聊天和请求屏幕中,您有两个 TabBar 屏幕:
- 聊天屏幕
- 请求屏幕
在主配置文件屏幕中,您有:
- 设置屏幕
- 编辑配置文件屏幕
PageView 代码片段:
@override
void initState() {
pageController = PageController(initialPage: _currentIndex);
chatAndRequestController = TabController(length: 2, vsync: this);
var chatAndRequestProvider =
Provider.of<ChatAndRequestProvider>(context, listen: false);
super.initState();
fbm.requestNotificationPermissions();
fbm.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
bool isRequest;
var mode = (Platform.isIOS) ? message['mode'] : message['data']['mode'];
var imageUrl = '';
switch (mode) {
case 'chat':
isRequest = false;
imageUrl =
chatAndRequestProvider.chatsList.first[kProfilePictureUrl];
break;
case 'sentRequest':
isRequest = true;
imageUrl = (Platform.isIOS)
? message['profilePictureUrl']
: message['data']['profilePictureUrl'];
break;
case 'acceptRequest':
isRequest = false;
imageUrl = (Platform.isIOS)
? message['profilePictureUrl']
: message['data']['profilePictureUrl'];
break;
default:
isRequest = false;
break;
}
AudioCache player = new AudioCache();
const alarmAudioPath = "sounds/notification_sound.mp3";
player.play(alarmAudioPath);
print('Show this ting');
if (_currentIndex != 1) {
if (!isDialogOpen) {
isDialogOpen = true;
_showAnimatedBox(
context,
(Platform.isIOS)
? message['aps']['alert']['title']
: message['notification']['title'],
(Platform.isIOS)
? message['aps']['alert']['body']
: message['notification']['body'],
imageUrl,
isRequest,
);
}
}
},
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
},
);
notificationPlugin
.setListenerForLowerVersions(onNotificationInLowerVersions);
notificationPlugin.setOnNotificationClick(onNotificationClick);
_children.addAll([
MainFeedScreen(
analytics: widget.analytics,
observer: widget.observer,
latitude: widget.latitude,
longitude: widget.longitude,
),
MainChatAndRequestScreen(
analytics: widget.analytics,
observer: widget.observer,
pageContoller: chatAndRequestController,
),
MainProfileScreen(analytics: widget.analytics, observer: widget.observer),
]);
}
Future _showAnimatedBox(context, topText, bottomText, imageUrl, isRequest) {
showDialog(
context: context,
builder: (BuildContext builderContext) {
_timer = Timer(Duration(seconds: 4), () {
Navigator.of(context).pop();
isDialogOpen = false;
});
return Dismissible(
key: Key('dismissible'),
direction: DismissDirection.up,
onDismissed: (_) {
Navigator.of(context).pop();
isDialogOpen = false;
},
child: FunkyNotification(
() {
var chatAndRequestProvider =
Provider.of<ChatAndRequestProvider>(context, listen: false);
// var contextProv =
// Provider.of<ContextProvider>(context, listen: false);
chatAndRequestProvider.setAreThereNewChatsAndRequestFalse();
if (isRequest) {
pageController.jumpToPage(1);
chatAndRequestController.animateTo(1);
Navigator.of(context).pop();
// Navigator.of(contextProv.context).pop();
// SystemChannels.platform.invokeMethod('SystemNavigator.pop');
// Navigator.popUntil(
// context,
// ModalRoute.withName('/mainProfileScreen'),
// );
// Navigator.of(context)
// .popUntil(ModalRoute.withName('/mainProfileScreen'));
// Navigator.pushAndRemoveUntil(
// context,
// MaterialPageRoute(
// builder: (BuildContext context) => MainTabBarController(
// analytics: null,
// observer: null,
// latitude: 100.23423234,
// longitude: 12.324234234,
// isProfileBlocked: false,
// isVersionGood: true,
// ),
// ),
// (route) => true,
// );
} else {
var chatAndRequestProvider =
Provider.of<ChatAndRequestProvider>(context,
listen: false);
pageController.jumpToPage(1);
chatAndRequestController.animateTo(0);
Navigator.of(context).pop();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatScreen(
appMode:
chatAndRequestProvider.chatsList.first[kAppMode],
peerId: chatAndRequestProvider.chatsList.first[kUserId],
peerAvatar: chatAndRequestProvider
.chatsList.first[kProfilePictureUrl],
peerName: chatAndRequestProvider
.chatsList.first[kNameAndSurname],
friendshipStatus: chatAndRequestProvider
.chatsList.first['friendsStatus'],
analytics: widget.analytics,
observer: widget.observer,
),
),
);
}
},
topText,
bottomText,
imageUrl,
),
);
}).then((val) {
if (_timer.isActive) {
_timer.cancel();
}
isDialogOpen = false;
});
}
我会尽量让我的回答尽可能笼统,以便其他人更容易理解。
简而言之,问题是您有一组嵌套的屏幕分布在一组综合浏览量之间,并且您想通过外部事件(在本例中为覆盖)在综合浏览量之间切换。
下面是一个例子:
TL;DR
我无法提供完整的代码,因为我没有您的完整源代码。但这里有一个例子
注:本例使用Provider.
示例事件代码
// Remove all the screens in the route
Navigator.of(context).popUntil((route) => route.isFirst); // If the screen is not the first replace the check
// Change the second pageview page
Provider.of<ChatSelectPageView>(context, listen: false).setPage(selectedSecondPageViewPage);
// In case it is required to add intermediate screens between the first and the second pageview it must be added here
// Change the main pageview page
_mainPageViewcontroller.animateToPage(1);
第二个 PageView
// Reads the page index present in the provider
int selectedPage = Provider.of<ChatSelectPageView>(context, listen: false).page;
// Changes to the cotroller page to the selected page
_pageController.jumpToPage(selectedPage);
ChatSelectPageView
class ChatSelectPageView extends ChangeNotifier {
int page = 0;
void setPage(int _page) {
page = _page;
// Depending on your implementation it might be better to remove this line to reduce the number of builds
notifyListeners();
}
}
TS;WM
为了实现所需的行为,有多种方法可以实现。如果我们想坚持您的实施,我们将受到一些限制。但在这种情况下,我建议你做的是使用某种全局状态管理库,例如提供程序,它可以在没有任何库的情况下完成,但状态很快就会变得非常混乱。
正如您上面提到的,您尝试了 Navigator.popUntil
但它没有用,我怀疑这是因为您提供了错误的上下文。由于 Navigator.**** 依赖上下文来工作,即要弹出屏幕,您必须提供其上下文。或者路由检查错误
这段代码要写在你的情况下的外部事件中,它将写在叠加层的点击侦听器中。
使用 Provider 等状态管理解决方案将状态传递给主网页浏览的后代,直至屏幕。该提供商的类型为 ChangeNotifierProvider
。单击叠加层时,将设置一个标志作为所需的综合浏览量页面索引(我说的是第二次综合浏览量)。在您的情况下,此标志用于 select 聊天或请求。
完成后调用 Navigator.of(context).popUntil((route) => route.isFirst);
假设综合浏览量出现在应用程序的第一页上。如果它不在该页面上,您将不得不使用 Navigator.of(context).popUntil()
和自定义逻辑。
之后,我们将不得不导航回第 2 个综合浏览量,或将第一个综合浏览量更改为您的第 2 个页面。因为我们之前在 provider 中更改了标志,所以第二次 pageview 将已经切换。
您好, 我有一个非常具体的问题要问。我必须用步骤和图片来解释它,所以它们就在那里。 我有一个包含三个屏幕的应用程序:
主提要屏幕,
主聊天和请求屏幕,
主配置文件屏幕,
它们都是 PageView 的一部分。此 PageView class 在名为 main_tab_controller.dart 的 class 中进行控制。在 class 中,在 initState() 中,我有一个 Firebase 消息传递方法,每次收到通知 (onMessage) 时都会调用该方法。因此,每次我收到此通知时,我都会显示一个如下所示的叠加层。
并且它在这三个主屏幕上完美运行。如果是聊天通知,我会将 PageView 定向到第二个屏幕,即 MainChatAndRequest 屏幕,然后打开聊天屏幕。如果是请求通知,我会将 PageView 定向到第二个屏幕,即 MainChatAndRequest 屏幕,然后打开请求屏幕。
但我遇到的问题如下。在我的 MainFeedScreen 和 MainProfileScreen 中,我打开了一些其他屏幕。例如,在 MainFeedScreen 中,我打开 UserDetailsScreen 或 FilterScreen。或者在 MainProfileScreen 中,我打开 SettingsScreen 或 EditUserProfileScreen。
所以我的问题是:例如,如果我导航到 MainProfileScreen 并在该屏幕中打开 SettingsScreen,并且我收到叠加顶部消息,我如何关闭当前打开的 SettingsScreen 并导航回第二个屏幕即,main_tab_controller.dart 的 initState() 中的 Firebase 消息传递函数的 MainChatsAndRequestsScreen 是所有其他屏幕的 parent。 您有下图:
我什么都试过了,Navigator.popUntil(context),Navigator.pushReplacement(context),用过Navigator.pushNamed(context),但都没有用。如果有人能帮助我,我将不胜感激。
只是为了让您更好地理解屏幕: Parent 屏幕是具有三个屏幕的 PageView:
- 主提要屏幕
- 主聊天和请求屏幕
- 主配置文件屏幕
然后在主提要屏幕中您有:
- 过滤屏幕
- 个人资料详细信息屏幕
在主聊天和请求屏幕中,您有两个 TabBar 屏幕:
- 聊天屏幕
- 请求屏幕
在主配置文件屏幕中,您有:
- 设置屏幕
- 编辑配置文件屏幕
PageView 代码片段:
@override
void initState() {
pageController = PageController(initialPage: _currentIndex);
chatAndRequestController = TabController(length: 2, vsync: this);
var chatAndRequestProvider =
Provider.of<ChatAndRequestProvider>(context, listen: false);
super.initState();
fbm.requestNotificationPermissions();
fbm.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
bool isRequest;
var mode = (Platform.isIOS) ? message['mode'] : message['data']['mode'];
var imageUrl = '';
switch (mode) {
case 'chat':
isRequest = false;
imageUrl =
chatAndRequestProvider.chatsList.first[kProfilePictureUrl];
break;
case 'sentRequest':
isRequest = true;
imageUrl = (Platform.isIOS)
? message['profilePictureUrl']
: message['data']['profilePictureUrl'];
break;
case 'acceptRequest':
isRequest = false;
imageUrl = (Platform.isIOS)
? message['profilePictureUrl']
: message['data']['profilePictureUrl'];
break;
default:
isRequest = false;
break;
}
AudioCache player = new AudioCache();
const alarmAudioPath = "sounds/notification_sound.mp3";
player.play(alarmAudioPath);
print('Show this ting');
if (_currentIndex != 1) {
if (!isDialogOpen) {
isDialogOpen = true;
_showAnimatedBox(
context,
(Platform.isIOS)
? message['aps']['alert']['title']
: message['notification']['title'],
(Platform.isIOS)
? message['aps']['alert']['body']
: message['notification']['body'],
imageUrl,
isRequest,
);
}
}
},
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
},
);
notificationPlugin
.setListenerForLowerVersions(onNotificationInLowerVersions);
notificationPlugin.setOnNotificationClick(onNotificationClick);
_children.addAll([
MainFeedScreen(
analytics: widget.analytics,
observer: widget.observer,
latitude: widget.latitude,
longitude: widget.longitude,
),
MainChatAndRequestScreen(
analytics: widget.analytics,
observer: widget.observer,
pageContoller: chatAndRequestController,
),
MainProfileScreen(analytics: widget.analytics, observer: widget.observer),
]);
}
Future _showAnimatedBox(context, topText, bottomText, imageUrl, isRequest) {
showDialog(
context: context,
builder: (BuildContext builderContext) {
_timer = Timer(Duration(seconds: 4), () {
Navigator.of(context).pop();
isDialogOpen = false;
});
return Dismissible(
key: Key('dismissible'),
direction: DismissDirection.up,
onDismissed: (_) {
Navigator.of(context).pop();
isDialogOpen = false;
},
child: FunkyNotification(
() {
var chatAndRequestProvider =
Provider.of<ChatAndRequestProvider>(context, listen: false);
// var contextProv =
// Provider.of<ContextProvider>(context, listen: false);
chatAndRequestProvider.setAreThereNewChatsAndRequestFalse();
if (isRequest) {
pageController.jumpToPage(1);
chatAndRequestController.animateTo(1);
Navigator.of(context).pop();
// Navigator.of(contextProv.context).pop();
// SystemChannels.platform.invokeMethod('SystemNavigator.pop');
// Navigator.popUntil(
// context,
// ModalRoute.withName('/mainProfileScreen'),
// );
// Navigator.of(context)
// .popUntil(ModalRoute.withName('/mainProfileScreen'));
// Navigator.pushAndRemoveUntil(
// context,
// MaterialPageRoute(
// builder: (BuildContext context) => MainTabBarController(
// analytics: null,
// observer: null,
// latitude: 100.23423234,
// longitude: 12.324234234,
// isProfileBlocked: false,
// isVersionGood: true,
// ),
// ),
// (route) => true,
// );
} else {
var chatAndRequestProvider =
Provider.of<ChatAndRequestProvider>(context,
listen: false);
pageController.jumpToPage(1);
chatAndRequestController.animateTo(0);
Navigator.of(context).pop();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatScreen(
appMode:
chatAndRequestProvider.chatsList.first[kAppMode],
peerId: chatAndRequestProvider.chatsList.first[kUserId],
peerAvatar: chatAndRequestProvider
.chatsList.first[kProfilePictureUrl],
peerName: chatAndRequestProvider
.chatsList.first[kNameAndSurname],
friendshipStatus: chatAndRequestProvider
.chatsList.first['friendsStatus'],
analytics: widget.analytics,
observer: widget.observer,
),
),
);
}
},
topText,
bottomText,
imageUrl,
),
);
}).then((val) {
if (_timer.isActive) {
_timer.cancel();
}
isDialogOpen = false;
});
}
我会尽量让我的回答尽可能笼统,以便其他人更容易理解。
简而言之,问题是您有一组嵌套的屏幕分布在一组综合浏览量之间,并且您想通过外部事件(在本例中为覆盖)在综合浏览量之间切换。
下面是一个例子:
TL;DR
我无法提供完整的代码,因为我没有您的完整源代码。但这里有一个例子
注:本例使用Provider.
示例事件代码
// Remove all the screens in the route
Navigator.of(context).popUntil((route) => route.isFirst); // If the screen is not the first replace the check
// Change the second pageview page
Provider.of<ChatSelectPageView>(context, listen: false).setPage(selectedSecondPageViewPage);
// In case it is required to add intermediate screens between the first and the second pageview it must be added here
// Change the main pageview page
_mainPageViewcontroller.animateToPage(1);
第二个 PageView
// Reads the page index present in the provider
int selectedPage = Provider.of<ChatSelectPageView>(context, listen: false).page;
// Changes to the cotroller page to the selected page
_pageController.jumpToPage(selectedPage);
ChatSelectPageView
class ChatSelectPageView extends ChangeNotifier {
int page = 0;
void setPage(int _page) {
page = _page;
// Depending on your implementation it might be better to remove this line to reduce the number of builds
notifyListeners();
}
}
TS;WM
为了实现所需的行为,有多种方法可以实现。如果我们想坚持您的实施,我们将受到一些限制。但在这种情况下,我建议你做的是使用某种全局状态管理库,例如提供程序,它可以在没有任何库的情况下完成,但状态很快就会变得非常混乱。
正如您上面提到的,您尝试了 Navigator.popUntil
但它没有用,我怀疑这是因为您提供了错误的上下文。由于 Navigator.**** 依赖上下文来工作,即要弹出屏幕,您必须提供其上下文。或者路由检查错误
这段代码要写在你的情况下的外部事件中,它将写在叠加层的点击侦听器中。
使用 Provider 等状态管理解决方案将状态传递给主网页浏览的后代,直至屏幕。该提供商的类型为 ChangeNotifierProvider
。单击叠加层时,将设置一个标志作为所需的综合浏览量页面索引(我说的是第二次综合浏览量)。在您的情况下,此标志用于 select 聊天或请求。
完成后调用 Navigator.of(context).popUntil((route) => route.isFirst);
假设综合浏览量出现在应用程序的第一页上。如果它不在该页面上,您将不得不使用 Navigator.of(context).popUntil()
和自定义逻辑。
之后,我们将不得不导航回第 2 个综合浏览量,或将第一个综合浏览量更改为您的第 2 个页面。因为我们之前在 provider 中更改了标志,所以第二次 pageview 将已经切换。