firebase - AutoDisposeStreamProvider is not being disposed at loggin out
问题描述
Currently, we are using Firebase to implement a simple chat on our application.
We handle the application's launch and authentication with Riverpod.
Launching goes like as follows:
@override
Widget build(BuildContext context) {
LocalNotificationService()
.handleApplicationWasLaunchedFromNotification(_onSelectNotification);
LocalNotificationService().setOnSelectNotification(_onSelectNotification);
_configureDidReceiveLocalNotification();
// final navigator = useProvider(navigatorProvider);
final Settings? appSettings = useProvider(settingsNotifierProvider);
final bool darkTheme = appSettings?.darkTheme ?? false;
final LauncherState launcherState = useProvider(launcherProvider);
SystemChrome.setEnabledSystemUIOverlays(
<SystemUiOverlay>[SystemUiOverlay.bottom],
);
return MaterialApp(
title: 'Thesis Cancer',
theme: darkTheme ? ThemeData.dark() : ThemeData.light(),
navigatorKey: _navigatorKey,
debugShowCheckedModeBanner: false,
home: Builder(
builder: (BuildContext context) => launcherState.when(
loading: () => SplashScreen(),
needsProfile: () => LoginScreen(),
profileLoaded: () => MainScreen(),
),
),
);
}
Currently, we just enable logging out from main screen and rooms screen as follows:
ListTile(
leading: const Icon(Icons.exit_to_app),
title: const Text('Çıkış yap'),
onTap: () =>
context.read(launcherProvider.notifier).signOut(),
),
Where signOut
does:
Future<void> signOut() async {
tokenController.state = '';
userController.state = User.empty;
await dataStore.removeUserProfile();
_auth.signOut();
state = const LauncherState.needsProfile();
}
The problem is, every time we goes to the RoomsPage
and we do logout from it or from the main page (coming back from rooms), we get the same problem with firebase:
The caller does not have permission to execute the specified operation.
.
Of course, signout
closes the Firebase, thence Firebase throws this error; but, it is supposed after coming out from the RoomsScreen (it happens even when go back to the main screen), this widget is disposed therefore the connection should be closed, disposed, but it seems it is still on memory.
The RoomPage screen is as follows:
class RoomsPage extends HookWidget {
@override
Widget build(BuildContext context) {
final AsyncValue<List<fc_types.Room>> rooms =
useProvider(roomsListProvider);
return Scaffold(
appBar: Header(
pageTitle: "Uzmanlar",
leading: const BackButton(),
),
endDrawer: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 275),
child: SideMenu(),
),
body: rooms.when(
data: (List<fc_types.Room> rooms) {
if (rooms.isEmpty) {
return Container(
alignment: Alignment.center,
margin: const EdgeInsets.only(
bottom: 200,
),
child: const Text('No rooms'),
);
}
return ListView.builder(
itemCount: rooms.length,
itemBuilder: (
BuildContext context,
int index,
) {
final fc_types.Room room = rooms[index];
return GestureDetector(
onTap: () => pushToPage(
context,
ChatPage(
room: room,
),
),
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 8,
),
child: Row(
children: <Widget>[
Container(
height: 40,
margin: const EdgeInsets.only(
right: 16,
),
width: 40,
child: ClipRRect(
borderRadius: const BorderRadius.all(
Radius.circular(20),
),
child: Image.network(room.imageUrl ?? ''),
),
),
Text(room.name ?? 'Room'),
],
),
),
);
},
);
},
loading: () => const Center(
child: CircularProgressIndicator(),
),
error: (Object error, StackTrace? stack) => ErrorScreen(
message: error.toString(),
actionLabel: 'Home',
onPressed: () => Navigator.of(context).pop(),
),
),
);
}
}
And the provider is simple:
final AutoDisposeStreamProvider<List<fc_types.Room>> roomsListProvider =
StreamProvider.autoDispose<List<fc_types.Room>>(
(_) async* {
final Stream<List<fc_types.Room>> rooms = FirebaseChatCore.instance.rooms();
await for (final List<fc_types.Room> value in rooms) {
yield value;
}
},
name: "List Rooms Provider",
);
I suppose the AutoDispose constructor makes this provider auto disposed when the widget is removed, so, it should close the connection with Firebase (as de documentation says).
WHat's the problem here?
What am i missing?
Should i open an issue about this?
解决方案
在文档中,该示例使用Stream
基于 aStreamController
final messageProvider = StreamProvider.autoDispose<String>((ref) async* {
// Open the connection
final channel = IOWebSocketChannel.connect('ws://echo.websocket.org');
// Close the connection when the stream is destroyed
ref.onDispose(() => channel.sink.close());
// Parse the value received and emit a Message instance
await for (final value in channel.stream) {
yield value.toString();
}
});
在您的情况下,您的方法返回一个Stream
. 这改变了游戏规则。只需返回Stream
.
final AutoDisposeStreamProvider<List<fc_types.Room>> roomsListProvider =
StreamProvider.autoDispose<List<fc_types.Room>>(
(_) => FirebaseChatCore.instance.rooms(),
name: "List Rooms Provider",
);
推荐阅读
- sql - 使用表中的表达式作为没有结果的选择语句
- scala - 来自另一个 DF(或来自 HBase)的“丰富”Spark DataFrame
- angular - Angular 单元测试 - 使用 Stub 模拟子组件
- python - 如何简化按索引搜索列表项?
- json - 将 dict 元组拆分为数据框中的单个记录
- javascript - 使用循环将图层添加到我的传单地图
- javascript - vue-v-bind:类。. 如何对计算属性对象(嵌套)的值做出反应?
- java - 2 个音符之间的 JFugue 半音
- android - You must call setGraph() before calling getGraph()
- python - 用于更改 Outlook 邮件项目主题行的 Python 函数