I am trying to use the package page_flip_builder to display my notifications in a drawer. The only problem is that animation builds multiple times so the GetX Provider also asks multiple times for his Refresh Controller. The screen works great if I use it outside the page_flip_builder animation. Is there a way to overcome it?
The error I am getting:
Don't use one refreshController to multiple SmartRefresher,It will cause some unexpected bugs mostly in TabBarView
The Drawer:
import 'package:flutter/material.dart';
import 'package:lazy_load_indexed_stack/lazy_load_indexed_stack.dart';
import 'package:page_flip_builder/page_flip_builder.dart';
import '../imports.dart';
import '../modules/chat/pages/chats/chat_list.dart';
import '../modules/chat/pages/groups/groups_list.dart';
import '../modules/notifications/pages/index.dart';
import 'menu_page.dart';
class HomeDrawer extends StatefulWidget {
@override
_HomeDrawerState createState() => _HomeDrawerState();
}
class _HomeDrawerState extends State<HomeDrawer> {
final pageFlipKey = GlobalKey<PageFlipBuilderState>();
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Drawer(
child: Container(
height: context.height,
color: theme.primaryColorDark,
child: SafeArea(
child: Container(
color: Colors.black,
child: PageFlipBuilder(
key: pageFlipKey,
frontBuilder: (_) => MenuPage(
onFlip: () => pageFlipKey.currentState?.flip(),
),
backBuilder: (_) =>
NotificationScreen(
onFlip: () => pageFlipKey.currentState?.flip(),
),
maxTilt: 0.003,
maxScale: 0.2,
onFlipComplete: (isFrontSide) => print('front: $isFrontSide'),
),
),
),
),
);
}
}
Menu page:
import 'package:flutter/material.dart';
import '../imports.dart';
class MenuPage extends StatelessWidget {
final VoidCallback? onFlip;
const MenuPage({Key? key, this.onFlip}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final whiteColor = Colors.white;
return Drawer(
child: Container(
height: context.height,
decoration: BoxDecoration(gradient: AppStyles.primaryGradient),
child: SafeArea(
child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
BottomFlipIconButton(onFlip: onFlip),
SizedBox(
// color: Colors.redAccent,
height: 50,
child: Align(
alignment: Alignment.bottomRight,
child: IconButton(
padding: EdgeInsets.only(right: 15),
constraints: BoxConstraints(),
onPressed: () => Get.back(),
icon: Icon(
menuIcons['exit'],
color: whiteColor,
),
),
),
),
MenuTile(
title: menuStrings['notifications']!,
icon: menuIcons['notifications'],
onTap: () {
onFlip?.call();
},
),
],
),
),
),
);
}
}
final Map<String, IconData> menuIcons = {
'notifications': Icons.notifications,
};
final Map<String, String> menuStrings = {
'notifications': 'Notifications',
};
class MenuTile extends StatelessWidget {
final IconData? icon;
final double? iconSize;
final String? title;
final bool isPadding;
final void Function()? onTap;
const MenuTile(
{Key? key,
this.icon,
this.title,
this.onTap,
this.iconSize,
this.isPadding = true})
: super(key: key);
@override
Widget build(BuildContext context) {
return ListTile(
dense: true,
tileColor: Colors.redAccent,
horizontalTitleGap: 8,
contentPadding:
EdgeInsets.symmetric(horizontal: 36, vertical: isPadding ? 8 : 0),
leading: Icon(
icon,
color: Colors.white.withOpacity(0.5),
size: iconSize,
),
title: Text(
title == null ? '' : title!.toUpperCase(),
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
color: AppStyles.whiteMenuColor,
letterSpacing: 3,
),
),
onTap: onTap,
);
}
}
class BottomFlipIconButton extends StatelessWidget {
const BottomFlipIconButton({Key? key, this.onFlip}) : super(key: key);
final VoidCallback? onFlip;
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
onPressed: onFlip,
icon: const Icon(Icons.flip),
)
],
);
}
}
Notification Screen:
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import '../../../imports.dart';
import '../models/notification.dart';
import '../provider.dart';
import 'widget/item.dart';
class NotificationScreen extends StatelessWidget {
final VoidCallback? onFlip;
const NotificationScreen({Key? key, this.onFlip}) : super(key: key);
@override
Widget build(BuildContext context) {
print('building notifications page');
final theme = Theme.of(context);
return SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
IconButton(
onPressed: () {
onFlip?.call();
// appPrefs.drawerState(DrawerState.Menu);
},
icon: Icon(Icons.arrow_back),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 4, bottom: 8, left: 20),
child: Text(
t.Notifications,
style: GoogleFonts.basic(
textStyle: theme.textTheme.headline5,
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
Icons.notifications,
),
),
],
),
Expanded(
child: Obx(
() => StreamBuilder<List<NotificationModel>>(
stream: notificationProvider.notsStream,
builder: (_, snapshot) {
final nots = snapshot.data ?? [];
return SmartRefresher(
controller: notificationProvider.refreshController,
enablePullDown: false,
enablePullUp: notificationProvider.limit() <= nots.length,
onLoading: notificationProvider.onLoadMore,
child: ListView.builder(
itemCount: nots.length,
itemBuilder: (_, i) => NotificationItem(nots[i]),
),
);
},
),
),
),
],
),
);
}
}
Notification Provider:
import 'dart:async';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import '../../imports.dart';
import 'data/notifications.dart';
import 'models/notification.dart';
NotificationProvider get notificationProvider =>
Get.find<NotificationProvider>();
class NotificationProvider {
RefreshController refreshController = RefreshController();
StreamSubscription<List<NotificationModel>>? notListener;
void init() {
refreshController = RefreshController();
notListener = NotificationRepo.notsStream(2)
.listen(NotificationRepo.checkIfToShowNotification);
}
final limit = 20.obs;
Stream<List<NotificationModel>> get notsStream =>
NotificationRepo.notsStream(limit());
void onLoadMore() {
limit.value += 20;
refreshController.loadComplete();
}
Future<void> dispose() async {
refreshController.dispose();
await notListener?.cancel();
notListener = null;
}
}