I'm trying to use Provider with MVVM architecture, I have a very strange error with provider, on the main page there are 3 sections, banner, discounts and categories, when I change the quantity of goods in discounts, everything works, but after switching to a category and back, it already gives an error when I change the quantity of goods,
the data is not null, I think the problem is with the provider, the scheme is as follows:
discount section-> quantity of goods -> works
home-> category-> go back-> quantity of goods in discounts-> not working
Demo project
The following _CastError was thrown building MainPage(dirty, dependencies: [_InheritedProviderScope<AllGoodsViewModel?>, _LocalizationsScope-[GlobalKey#a9792], MediaQuery, _InheritedProviderScope<MainPageListViewModel?>], state: _MainPageState#eb568):
Null check operator used on a null value
The relevant error-causing widget was:
MainPage MainPage:file:///Users/.../lib/main.dart:93:21
When the exception was thrown, this was the stack:
#0 Element.widget (package:flutter/src/widgets/framework.dart:3229:31)
#1 debugCheckHasMediaQuery.<anonymous closure> (package:flutter/src/widgets/debug.dart:245:17)
#2 debugCheckHasMediaQuery (package:flutter/src/widgets/debug.dart:261:4)
#3 MediaQuery.of (package:flutter/src/widgets/media_query.dart:908:12)
#4 ScreenUtil.screenWidth (package:flutter_screenutil/src/screen_util.dart:148:37)
#5 ScreenUtil.scaleWidth (package:flutter_screenutil/src/screen_util.dart:167:28)
#6 ScreenUtil.setWidth (package:flutter_screenutil/src/screen_util.dart:182:41)
#7 SizeExtension.w (package:flutter_screenutil/src/size_extension.dart:9:32)
#8 _MainPageState.build (package:.../View/MainPage.dart:240:78)
#9 StatefulElement.build (package:flutter/src/widgets/framework.dart:4919:27)
#10 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4806:15)
#11 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4977:11)
#12 Element.rebuild (package:flutter/src/widgets/framework.dart:4529:5)
#13 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2659:19)
#14 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:891:21)
#15 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:370:5)
#16 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1146:15)
#17 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1083:9)
#18 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:997:5)
#22 _invoke (dart:ui/hooks.dart:151:10)
#23 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:308:5)
#24 _drawFrame (dart:ui/hooks.dart:115:31)
(elided 3 frames from dart:async)
ScreenUtilInitService
class ScreenUtilInitService z{
/// A helper widget that initializes [ScreenUtil]
ScreenUtilInitService({required this.builder, Key? key,}) : super(key: key);
final Widget Function(BuildContext) builder;
#override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: Size(375, 812),
builder: (context, widget) => builder(context)
);
}
}
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _initialized = false;
bool _error = false;
Widget _present = SplashScreen();
void initializeFlutterFire() async {
try {
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await message();
await rootWidget();
setState(() {
_initialized = true;
});
} catch(e) {
setState(() {
_error = true;
});
}
}
Future<void> rootWidget() async {
final prefs = await SharedPreferences.getInstance();
final id = prefs.get("idAddress");
final logged = prefs.getBool("logged") ?? false;
if ((FirebaseAuth.instance.currentUser?.uid != null && logged) || id != null) {
setState(() {
_present = MainPage();
});
return;
} else {
setState(() {
_present = OfferPage();
});
return;
}
}
#override
void initState() {
initializeDB();
initializeFlutterFire();
super.initState();
listeners();
}
#override
Widget build(BuildContext context) {
if(_error) {
return MaterialApp(home:SplashScreen());
}
if (!_initialized) {
return MaterialApp(home:SplashScreen());
}
return RootPage().mainPage(present: _present);
}
}
and RootPage
class RootPage {
Widget mainPage({Widget? present}){
return MaterialApp(
initialRoute: '/',
routes: {
'/ProfilePage': (context) => ProfilePage(),
'/MainPage': (context) => MainPage(),
'/CartPage': (context) => CartPage(),
},
builder: (context, widget) {
return ScreenUtilInitService(
builder: (context) => widget!
);
},
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => MainPageListViewModel(),
),
ChangeNotifierProvider(
create: (context) => CartViewModel(),
child: CartPage()
),
ChangeNotifierProvider(
create: (context) => AllGoodsViewModel(),
),
ChangeNotifierProvider(
create: (context) => GoodsViewModel(),
),
],
child: present != null ? present : MainPage(),
),
);
}
}
MainPage
#override
void initState() {
setting();
super.initState();
_scrollListener();
_notification();
}
#override
void dispose() {
_scrollController.removeListener(() { });
_scrollController.dispose();
NotificationCenter().unsubscribe('cart');
NotificationCenter().unsubscribe('address');
super.dispose();
}
void setting() async {
final cart = await SQFliteService.cart.getCount();
final address = await SQFliteService.location.current();
setState((){
_address = address;
showCart = cart == 0 ? false : true;
});
Provider.of<MainPageListViewModel>(context, listen: false).deliveryRequest() ;
Provider.of<MainPageListViewModel>(context, listen: false).fetchBanner();
Provider.of<MainPageListViewModel>(context, listen: false).fetchCategory();
Provider.of<AllGoodsViewModel>(context, listen: false).fetchSale();
Provider.of<MainPageListViewModel>(context, listen: false).fetchLaunchMessage(context);
}
void _scrollListener(){
_scrollController.addListener(() {
///setState is null after pop after category
// setState(() {
// _bottomOffSet = _scrollController.offset;
// });
});
}
void _notification(){
NotificationCenter().subscribe('cart', () async {
final result = await SQFliteService.cart.getCount();
setState(() {
showCart = result == 0 ? false : true;
print("update");
});
});
NotificationCenter().subscribe('address', () async {
final result = await SQFliteService.location.current();
setState((){
_address = result;
});
});
}
#override
Widget build(BuildContext context) {
final models = Provider.of<MainPageListViewModel>(context);
final sale = Provider.of<AllGoodsViewModel>(context);
final size = MediaQuery.of(context).size;
return Container(
child: CustomScrollView(
physics: AlwaysScrollableScrollPhysics(),
controller: _scrollController,
slivers: [
SliverAppBar(
...
),
SliverList(
delegate: SliverChildBuilderDelegate((context, index) => Container(
color: Colors.transparent,
child: Column(
children: [
bannerW(),
ViewSale(model: sale.goods),
SizedBox(height: 10),
ViewCategory(model: models.category)
]
),
),
childCount: 1
),
),
],
),
);
ViewSale
class _ViewSaleState extends State<ViewSale> {
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
margin: EdgeInsets.only(left: 16, right: 16, bottom: 15),
child: Align(
alignment: Alignment.centerLeft,
child: Text("Скидки", style: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w900, fontSize: 18))),
),
Container(
height: ((MediaQuery.of(context).size.width/2.4) - 21) + 71,
width: MediaQuery.of(context).size.width,
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 16),
shrinkWrap: true,
itemCount: widget.model.length,
scrollDirection: Axis.horizontal,
itemBuilder: (item, index) {
return ChangeNotifierProvider(
create: (context) => AllGoodsViewModel(),
child: ViewGoodsSale(model: widget.model[index])
);
}),
)
]
);
}
}
ViewSaleGoods
class _ViewGoodsSaleState extends State<ViewGoodsSale> {
GlobalKey _key = GlobalKey();
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){
Provider.of<AllGoodsViewModel>(context, listen: false).setting(widget.model);
});
}
#override
Widget build(BuildContext context) {
final model = Provider.of<AllGoodsViewModel>(context);
final size = MediaQuery.of(context).size;
Widget inCart(){
return Container(
key: _key,
height: 31,
child: GestureDetector(
onPanDown: (details) {
Goods? item = widget.model;
RenderBox _cardBox = _key.currentContext!.findRenderObject() as RenderBox;
final localPosition = details.localPosition;
final localDx = localPosition.dx;
if (localDx <= _cardBox.size.width/2) {
Goods value = cart.firstWhere((element) => element.id == item.id);
if (item.optState == 0 ? value.orderCount <= 1 : value.orderCount <= value.opt!.count) {
setState(() {
context.read<AllGoodsViewModel>().setCountInCart(0);
final ind = cart.indexWhere((element) => element.id == item.id);
if (ind != -1) {
cart[ind].orderCount = 0;
SQFliteService.cart.delete(cart[ind].id);
cart.removeAt(ind);
}
});
} else {
model.haveItem(item: item, operation: item.optState == 0 ? -1 : (-1 * value.opt!.count));
}
} else {
model.haveItem(item: item, operation: item.optState == 0 ? 1 : item.count);
}
},
child: TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Design.appColor),
padding: MaterialStateProperty.all(EdgeInsets.symmetric(vertical: 8, horizontal: 10)),
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
))
),
onPressed: (){},
child: Container(
child: RichText(
text: TextSpan(
text: "",
children:[
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(Icons.remove, size: 14, color: Colors.white),
),
TextSpan(
text: " ${widget.model.optState == 0 ? (widget.model.minPrice ?? widget.model.price) : widget.model.opt!.price} ₽ ",
style: TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w500,
fontFamily: "Inter"
),
),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(Icons.add, size: 14, color: Colors.white),
)
],
),
),
),
),
),// Your TextButton code goes here.
);
}
Widget noInCart(){
return Container(
key: _key,
height: 31,
child: TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(model.orderBg),
padding: MaterialStateProperty.all(EdgeInsets.symmetric(vertical: 8, horizontal: 10)),
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
))
),
onPressed: (){
Goods? item = widget.model;
model.haveItem(item: item, operation: item.optState == 0 ? 1 : item.count);
},
child: Container(
child: RichText(
text: TextSpan(
text: "${widget.model.optState == 0 ? widget.model.minPrice == null ? widget.model.price : widget.model.minPrice : widget.model.opt!.price} ₽ ",
style: TextStyle(
color: widget.model.minPrice != null ? Design.grey : Colors.black,
decoration: widget.model.optState == 0 && widget.model.minPrice != null ? TextDecoration.lineThrough : TextDecoration.none,
fontSize: 14,
fontWeight: FontWeight.w500,
fontFamily: "Inter"
),
children:[
TextSpan(
text: widget.model.minPrice == null ? "" : " ${widget.model.price} ₽",
style: TextStyle(
color: Colors.black,
decoration: TextDecoration.none,
fontSize: 14,
fontWeight: FontWeight.w500,
fontFamily: "Inter"
),
),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(Icons.add, size: 14, color: Colors.black),
style: TextStyle(
color: Colors.black,
decoration: TextDecoration.none,
),
)
],
),
),
),
),
);
}
Widget card({required Size size}) {
return Container(
color: Colors.white,
width: (size.width/2.4) - 11,
margin: EdgeInsets.only(right: 10),
child: Column(
children: [
Stack(
children: [
Container(
height: (size.width/2.4) - 21,
padding: EdgeInsets.all(1),
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.contain,
image: NetworkImage(widget.model.images.first)
),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
border: Border.all(
width: 1,
color: Design.lightGrey
),
),
),
Visibility(
visible: context.read<AllGoodsViewModel>().countInCart == 0 ? false : true,
child: Container(
height: (size.width/2.4) - 21,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.5),
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
child: Visibility(
visible: true,
child: Center(
child: model.orderCountText,
),
),
),
)
]
),
SizedBox(height: 5),
Align(
alignment: Alignment.centerLeft,
child: Container(
height: 29,
child: Text(widget.model.name,
maxLines: 2,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w700,
fontFamily: "Inter",
),
),
),
),
SizedBox(height: 6),
Align(
alignment: Alignment.centerLeft,
child: (context.read<AllGoodsViewModel>().countInCart == 0) ? noInCart() : inCart()
)
],
),
);
}
return card(size: size);
}
}
ViewCategory
class _ViewCategoryState extends State<ViewCategory> {
#override
Widget build(BuildContext context) {
return Container(
child: GridView.builder(
padding: EdgeInsets.all(16),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1),
itemCount: widget.model.length,
itemBuilder: (context, index) {
return GestureDetector(
child: Stack(
children: [
Container(
decoration: BoxDecoration(
color: Design.lightGrey,
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
margin: EdgeInsets.only(right: (index.isOdd ? 0 : 5) , left: (index.isOdd ? 5 : 0), bottom: 10 ),
child: Image.network(widget.model[index].category?.url ?? "")
),
Positioned(
left: 10,
right: 10,
top: 8,
child: Text(widget.model[index].category?.name ?? "", style: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w500, fontSize: 12.sp)),
)
]
),
onTap: () async {
if (widget.model[index].category != null) {
final data = widget.model[index].category!;
final category = CategoryData(id: data.id, name: data.name);
if (data.tags == null) {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) =>
ChangeNotifierProvider.value(value: AllGoodsViewModel(),
child: AllCategoryGoodsPage(category: category))
)
);
} else {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) =>
ChangeNotifierProvider.value(value: GoodsViewModel(),
child: GoodsPage(category: category))
)
);
}
FirebaseAnalytics.instance.logEvent(name: "Category", parameters: null);
}
}
);
},
),
);
}
}
"Give a man a fish, feed him for a day. Teach a man to fish, feed him for a lifetime."
Thus, I try to explain what to do generally.
Firstly, look at the error stacktrace. You only provide the error message without stacktrace - thus no wonder nobody can see which line of code throws the error.
P.S. If you cannot find the stack trace, search or create a separate question (in some strange cases stack trace is not easy to find).
When you get the stack trace, you can read which line of code has this null pointer error, and which lines are calling that function. Suppose it is a.dart line 123 throwing the error, then look at that line and examine why that happens.
You may want to do "logging" or "debugging" to dig out further clues.
Then the bug should be found and solved easily. If not, try to create a minimal reproducible sample - your current sample is still very big and it is hard for people to dig and find bug for you :)
Your IDE will usually have great stack trace debugging. If you are using vscode, you can turn on the following two options for your error tracking.
If you're using Android Studio. Run -> View Breakpoints.
"Null check operator used on a null value" is a error you get when you use a bang operator (!) on a nullable instance which wasn't initialized.
In your "ViewSaleGoods" file you have it at -
if (item.optState == 0 ? value.orderCount <= 1 : value.orderCount <= value.opt!.count) { // Here .....
setState(() {
context.read<AllGoodsViewModel>().setCountInCart(0);
final ind = cart.indexWhere((element) => element.id == item.id);
if (ind != -1) {
cart[ind].orderCount = 0;
SQFliteService.cart.delete(cart[ind].id);
cart.removeAt(ind);
}
});
} else {
model.haveItem(item: item, operation: item.optState == 0 ? -1 : (-1 * value.opt!.count)); // here...
}
TextSpan(
text: " ${widget.model.optState == 0 ? (widget.model.minPrice ?? widget.model.price) : widget.model.opt!.price} ₽ ", // and here ....
style: TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w500,
fontFamily: "Inter"
),
),
so anyone of those is turning out null so the error. Now to fix it you can use some ways like -
// Use variable and if check
var nullSafe;
if (value.opt != null) {
nullSafe = value.opt; // now you can use these variable instead
}
// Or use ?. and ??
value.opt?count ?? "some default value"
Hope it helps. 😄
I solved the problem, the error was not at all in the provider, but in ScreenUtilInitService I changed it to
class ScreenUtilInitService {
Widget build(Function(BuildContext) builder) {
return ScreenUtilInit(
designSize: Size(375, 812),
builder: (context, widget) => builder(context)
);
}
}
and removed ScreenUtilInitService from GoodsPage and AllCategoryGoodsPage so ScreenUtilInitService is not redefined as a widget, which resulted in the error
Related
I have an album screen that uses MultiBlocProvider to create three cubits, one for each tab of the album page. Each tab page reads its own cubit using context.read() and then uses the cubit in order to fetch some data asynchronously that will eventually also change the cubit's state.
I've noticed that although the app functions just fine, when switching between tabs, an error comes up in the logs stating “Looking up a deactivated widget's ancestor is unsafe…”. From what I've read, this is probably caused due to the context changing, which is a result of the widget being moved around in the widget tree. Is my understanding correct?
In order to give a more specific example, here's my album screen:
class AlbumScreen extends StatelessWidget {
const AlbumScreen({Key? key}) : super(key: key);
static const String path = '/albumScreen';
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<AlbumArtworksCubit>(
create: (BuildContext context) => AlbumArtworksCubit(),
),
BlocProvider<AlbumArtistsCubit>(
create: (BuildContext context) => AlbumArtistsCubit(),
),
BlocProvider<AlbumGalleriesCubit>(
create: (BuildContext context) => AlbumGalleriesCubit(),
),
],
child: const AlbumComponent(),
);
}
}
Here's its component:
class AlbumComponent extends StatefulWidget {
const AlbumComponent({Key? key}) : super(key: key);
#override
State<AlbumComponent> createState() => _AlbumComponentState();
}
class _AlbumComponentState extends State<AlbumComponent>
with SingleTickerProviderStateMixin {
TabController? _controller;
#override
void initState() {
super.initState();
_controller = TabController(length: 3, vsync: this, initialIndex: 0);
_controller?.addListener(() {
setState(() {});
});
}
#override
void dispose() {
_controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: PreferredSize(
preferredSize: const Size.fromHeight(100),
child: ArtVoltAppBar(
title: '',
hasLogo: true,
hasBorder: false,
actions: <Widget>[
Align(
alignment: Alignment.topRight,
child: GestureDetector(
onTap: () => NavigatorUtils.goToSettingsScreen(context),
child: Padding(
padding: const EdgeInsets.only(top: 12, right: 12),
child: Image.asset(
'assets/icons/settings_icon.png',
height: 24,
width: 24,
),
),
),
),
],
bottom: TabBar(
controller: _controller,
tabs: [
Tab(
child: Text(
'Artworks',
style: montserratSemiBold16.copyWith(
color: _controller?.index == 0
? AppColors.black
: AppColors.grey,
),
),
),
Tab(
child: Text(
'Artists',
style: montserratSemiBold16.copyWith(
color: _controller?.index == 1
? AppColors.black
: AppColors.grey,
),
),
),
Tab(
child: Text(
'Galleries',
style: montserratSemiBold16.copyWith(
color: _controller?.index == 2
? AppColors.black
: AppColors.grey,
),
),
),
],
labelColor: AppColors.black,
labelStyle: montserratSemiBold16.copyWith(color: AppColors.black),
indicatorColor: AppColors.black,
unselectedLabelColor: AppColors.red,
indicatorSize: TabBarIndicatorSize.label,
isScrollable: true,
indicatorWeight: 5,
),
),
),
body: Padding(
padding: const EdgeInsets.only(top: 16, bottom: 16),
child: TabBarView(
controller: _controller,
children: [
Artworks(
key: UniqueKey(),
),
Artists(
key: UniqueKey(),
),
Galleries(
key: UniqueKey(),
),
],
),
),
),
);
}
}
And here's one of the tabs (Galleries()) for the sake of keeping the post relatively small:
class Galleries extends StatefulWidget {
const Galleries({Key? key}) : super(key: key);
#override
State<Galleries> createState() => _GalleriesState();
}
class _GalleriesState extends State<Galleries> {
AlbumGalleriesCubit get _cubit => context.read<AlbumGalleriesCubit>();
List<AzItem<Gallery>> items = [];
List<String> _alphabet = [];
final IndexBarDragListener _indexListener = IndexBarDragListener.create();
String _topLetter = 'A';
String _lastIndexItemPressed = 'A';
#override
void initState() {
super.initState();
initData();
}
Future<void> initData() async {
await _cubit.fetchAllGalleries();
items = _cubit.galleries
.map(
(Gallery gallery) => AzItem<Gallery>(
data: gallery,
text: gallery.name,
tag: gallery.name[0].toUpperCase(),
),
)
.toList();
SuspensionUtil.sortListBySuspensionTag(items);
SuspensionUtil.setShowSuspensionStatus(items);
_alphabet = items
.map((AzItem<Gallery> item) => item.getSuspensionTag())
.toSet()
.toList();
_indexListener.dragDetails.addListener(() {
setState(() {
_topLetter = _indexListener.dragDetails.value.tag ?? 'A';
_lastIndexItemPressed = _indexListener.dragDetails.value.tag ?? 'A';
});
});
setState(() {});
}
#override
void dispose() {
_cubit.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
if (_cubit.galleries.isEmpty &&
(_cubit.state is! AlbumGalleriesLoaded$ ||
_cubit.state is AlbumGalleriesLoading$)) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
CircularProgressIndicator(color: AppColors.red),
SizedBox(
height: 10,
),
Text(
'Loading galleries',
style: montserratMedium14,
)
],
),
);
} else if (_cubit.galleries.isEmpty &&
_cubit.state is AlbumGalleriesLoaded$) {
return const Center(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24),
child: Text(
initialEmptyAlbumMsg,
style: montserratMedium14,
textAlign: TextAlign.center,
),
),
);
} else {
return Stack(
children: <Widget>[
AzListView(
data: items,
itemCount: items.length,
indexHintBuilder: (_, __) => const SizedBox.shrink(),
indexBarData: _alphabet,
indexBarDragListener: _indexListener,
indexBarOptions: const IndexBarOptions(
textStyle: TextStyle(color: AppColors.blue),
),
itemBuilder: (BuildContext context, int index) {
final AzItem<Gallery> item = items[index];
final String tag = item.getSuspensionTag();
final bool offstage = !item.isShowSuspension;
return Column(
children: <Widget>[
VisibilityDetector(
key: Key('$tag-$index'),
onVisibilityChanged: (VisibilityInfo info) {
if (info.key.toString() == '[<\'$tag-$index\'>]') {
if (info.visibleFraction == 0.0) {
if (_topLetter.compareTo(tag) < 0) {
if ((_alphabet.indexOf(tag) -
_alphabet.indexOf(_topLetter)) ==
1) {
setState(() => _topLetter = tag);
}
}
} else if (info.visibleFraction == 1.0) {
if (_topLetter.compareTo(tag) == 0 && tag != 'A') {
if (_lastIndexItemPressed != tag) {
setState(() {
_topLetter =
items[index - 1].getSuspensionTag();
_lastIndexItemPressed = 'A';
});
}
}
}
}
},
child: Offstage(
offstage: offstage,
child: Container(
height: 30,
color: Colors.white,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
alignment: Alignment.centerLeft,
child: Text(
tag,
style: montserratSemiBold16.copyWith(
color: AppColors.grey),
),
),
),
),
),
GestureDetector(
onTap: () => NavigatorUtils.goToGalleryViewScreen(
context,
gallery: item.data!,
),
child: Container(
color: AppColors.white,
child: Column(
children: <Widget>[
const AppDivider(),
Row(
children: <Widget>[
Container(
height: 56,
width: 56,
margin: const EdgeInsets.only(
left: 24, right: 16, top: 4, bottom: 4),
decoration: BoxDecoration(
color: AppColors.white,
border: Border.all(
width: 1, color: AppColors.grey),
borderRadius: BorderRadius.circular(8),
),
child: CachedNetworkImage(
placeholder: (context, url) => const Center(
child: CircularProgressIndicator(
color: AppColors.red,
),
),
errorWidget: (context, url, dynamic error) =>
const Icon(Icons.error),
imageUrl: item.data?.thumbnailPhoto ??
item.data?.firebasePhoto ??
'',
fit: BoxFit.contain,
),
),
Text(item.text, style: montserratMedium14),
],
)
],
),
),
),
],
);
},
),
Container(
height: 30,
color: AppColors.white,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
alignment: Alignment.centerLeft,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: <Color>[
AppColors.grey.withOpacity(.2),
AppColors.grey.withOpacity(.1),
],
),
),
child: Text(
_topLetter,
style: montserratSemiBold16.copyWith(color: AppColors.grey),
),
),
),
],
);
}
}
}
With the above in mind, if I navigate to the Galleries tab and then switch to another tab (i.e. Artists()), the error is the following:
[ ] E/flutter ( 4652): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
[ ] E/flutter ( 4652): At this point the state of the widget's element tree is no longer stable.
[ ] E/flutter ( 4652): To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
[ ] E/flutter ( 4652): #0 Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure>
package:flutter/…/widgets/framework.dart:4241
[ ] E/flutter ( 4652): #1 Element._debugCheckStateIsActiveForAncestorLookup
package:flutter/…/widgets/framework.dart:4255
[ ] E/flutter ( 4652): #2 Element.getElementForInheritedWidgetOfExactType
package:flutter/…/widgets/framework.dart:4286
[ ] E/flutter ( 4652): #3 Provider._inheritedElementOf
package:provider/src/provider.dart:339
[ ] E/flutter ( 4652): #4 Provider.of
package:provider/src/provider.dart:293
[ ] E/flutter ( 4652): #5 ReadContext.read
package:provider/src/provider.dart:649
[ ] E/flutter ( 4652): #6 _GalleriesState._cubit
package:artvolt_flutter/…/widgets/galleries.dart:28
[ ] E/flutter ( 4652): #7 _GalleriesState.initData
package:artvolt_flutter/…/widgets/galleries.dart:47
[ ] E/flutter ( 4652): <asynchronous suspension>
The error points to line 47 of the Galleries tab, which is:
items = _cubit.galleries
.map(
(Gallery gallery) => AzItem<Gallery>(
data: gallery,
text: gallery.name,
tag: gallery.name[0].toUpperCase(),
),
)
.toList();
As I understand, this part of the initData() call is still running despite me having moved to a different tab. The tab switch has caused the galleries widget to change position in the widget tree thus changing the context which in turn results to the error that I am seeing. Is this approximately correct?
If so, what is the best way to handle such lifecycle cases? Is there a way for me to stop all pending work on the Galleries widget when the cubit's context has changed in order to avoid the error? I read a bit about the didChangeDependencies() method but I'm not sure if I should use it to "fix" the error since the source may be my bad state management code.
i am having this kind of error in my app. I am using audioplayers package to play a short sound. But after playing for about 5 times I am getting an error, which is:
At this point the state of the widget's element tree is no longer stable.
To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
This is my code:
class NewOrders extends StatefulWidget {
#override
_NewOrdersState createState() => _NewOrdersState();
}
class _NewOrdersState extends State<NewOrders> {
final SocketService socketService = injector.get<SocketService>();
final _orderRepo = OrderRepo(type: 'Accepted');
int activeItem = 0;
AudioPlayer audioPlayer = AudioPlayer();
final player = AudioCache(prefix: 'assets/sounds/');
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<OrderBloc>(
create: (context) =>
OrderBloc(orderRepo: _orderRepo)..add(OrderLoadEvent()),
),
BlocProvider<OrderInfoBloc>(
create: (context) => OrderInfoBloc(),
)
],
child: Scaffold(
body: BlocBuilder<OrderBloc, OrderState>(
builder: (BuildContext context, state) {
if (state is OrderLoadingState) {
return Center(child: CustomLoading());
}
if (state is OrderLoadedState) {
if (state.loadedOrders.length > 0) {
OrderInfoRepo _orderInfoRepo =
OrderInfoRepo(tableId: state.loadedOrders[activeItem].id);
context
.read<OrderInfoBloc>()
.add(OrderInfoLoadEvent(orderInfoRepo: _orderInfoRepo));
socketService.socket.on(
'createOrder',
(data) => {
context.read<OrderBloc>().add(OrderLoadEvent()),
player.play('accomplished.mp3'),
Future.delayed(const Duration(milliseconds: 500), () => player.clear('accomplished.mp3'))
},
);
return Container(
child: Row(
children: [
Container(
decoration: BoxDecoration(
color: tertiaryColor,
boxShadow: [
BoxShadow(
color: Colors.grey,
offset: Offset.zero,
blurRadius: 1,
spreadRadius: 0.5)
],
),
child: ListView.builder(
itemBuilder: (context, int index) {
if (index == activeItem) {
return Container(
height: 50,
color: Colors.white,
child: TextButton(
onPressed: () {},
child: Container(
child: Text(
'${state.loadedOrders[index].tableNum} - стол',
style: TextStyle(
fontSize: 20, color: tertiaryColor),
),
),
),
);
} else {
return Container(
height: 50,
// color: tertiaryColor,
child: TextButton(
onPressed: () {
setState(() {
activeItem = index;
});
context.read<OrderInfoBloc>().add(
OrderInfoLoadEvent(
orderInfoRepo: _orderInfoRepo));
},
child: Container(
child: Text(
'${state.loadedOrders[index].tableNum} - стол',
style: TextStyle(
fontSize: 20, color: Colors.white),
),
),
),
);
}
},
itemCount: state.loadedOrders.length,
),
width: 250,
),
Expanded(
child: OrderInfoWidget(),
)
],
),
);
} else {
return EmptyCart();
}
} else {
return NetworkErrorWidget();
}
},
),
),
);
}
}
What is the reason is I am getting the above error?
For an Alert widget which has to be conditionally rebuilt every 3 seconds, on resetting the build state (_rebuild ) using an Immediate Invoking Function.
However the Immediate Invoking Function in which the Alert widget is wrapped
returns Null.
Since build(context) only accepts Widgets,is it even possible to use Immediate Invoking Function in build(context) to alter conditional parameters?
ERROR
Column's children must not contain any null values, but a null value was found at index 1
CODE
import 'package:flutter/material.dart';
import 'dart:async';
void main () => runApp(MaterialApp(
theme: ThemeData.dark(),
home: ConditionalWidgets(),
));
class ConditionalWidgets extends StatefulWidget {
#override
_ConditionalWidgetsState createState() => _ConditionalWidgetsState();
}
class _ConditionalWidgetsState extends State<ConditionalWidgets> {
bool _reBuild = true ;
int _counter = 0;
void initState(){
super.initState();
_reBuildTimer();
}
void _reBuildTimer() {
Timer.periodic(Duration(seconds: 3), (timer) {
setState() {
_counter++;
_reBuild = true;
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body : Center(child:Column(
children : [
Container(
margin: EdgeInsets.all(50),
padding: EdgeInsets.all(60),
decoration: BoxDecoration(
shape :BoxShape.rectangle,
border: Border.all(
color: Colors.orange,
width: 8,
),
borderRadius: BorderRadius.circular(10),
),
child:FittedBox(child: Text('Always Visible Widget', style: TextStyle()))),
// Conditional _reBuild Alert - returns NUll
if( _reBuild)
(){
AlertDialog(
title: Text('Conditionally Visible Widget'),
content: FittedBox(child: Text(
'Rebuild Count = $_counter', style: TextStyle())));
print('_reBuild = $_reBuild');
_reBuild = false;//Reset _reBuild using Immediate Invoking Function
}()
],
),),
);
}
}
I have something similar implemented for an app. I use streams to rebuild the widget
you can play with the code to get desired output
Alert Dialoge
showDialog(
context: context,
builder: (context) {
return StreamBuilder(
stream: bloc.eventStream,
builder: (context, snapshot) {
if (snapshot.data == Event.Success) {
return AlertDialog(
scrollable: true,
content: Column(
children: [
Icon(
Icons.check_circle,
color: Colors.green[800],
size: 300,
),
Text(
"Added Successfully !",
style: TextStyle(fontSize: 24),
)
],
),
);
} else if (snapshot.data == Event.Failed) {
return AlertDialog(
scrollable: true,
content: Column(
children: [
Icon(
Icons.cancel,
color: Colors.red[800],
size: 300,
),
Text(
"Invalid Fields",
style: TextStyle(fontSize: 24),
)
],
),
);
} else if (snapshot.data == Event.Error) {
return AlertDialog(
scrollable: true,
content: Column(
children: [
Icon(
Icons.warning_rounded,
color: Colors.yellow[800],
size: 300,
),
Text(
"Unknown Error",
style: TextStyle(fontSize: 24),
)
],
),
);
} else {
return AlertDialog(
scrollable: true,
content: Column(
children: [
SizedBox(
height: 150,
width: 150,
child: CircularProgressIndicator(strokeWidth: 5.0)),
Text(
"Loading ",
style: TextStyle(fontSize: 24),
)
],
),
);
}
});
},
);
stream controller
import 'dart:async';
enum Event{Loading,Failed,Error,Success}
class addEmpbloc{
final _eventStreamController = StreamController<Event>.broadcast();
StreamSink<Event> get eventSink => _eventStreamController.sink;
Stream<Event>get eventStream => _eventStreamController.stream;
void dispose(){
_eventStreamController.close();
}
}
void _reBuildTimer() {
Timer.periodic(Duration(seconds: 3), (timer) {
bloc.eventSink.add(Event.Success);
});
}
Instead of enum, u can use the data type of u r choice
Using bloc from rxdart: ^0.24.1
I am trying to save object on mysql. The first try the object get saved succefully, the second try, with a new object, it falling on formKey.currentState.save(). I am using GlobalKey<FormState>() in order to validate the form with Stream
My code is
class DetailGamePage extends StatefulWidget {
#override
_DetailGameState createState() => _DetailGameState();
}
class _DetailGameState extends State<DetailGamePage> {
final formKey = GlobalKey<FormState>();
GameBloc gameBloc;
#override
void didChangeDependencies() {
super.didChangeDependencies();
if (gameBloc == null) {
gameBloc = Provider.gameBloc(context);
}
}
#override
Widget build(BuildContext context) {
Game _game = ModalRoute.of(context).settings.arguments;
if (_game == null) {
_game = Game(
color: "#000000",
description: "",
env: "",
isBuyIt: false,
isOnBacklog: false);
}
return Scaffold(
appBar: AppBar(
iconTheme: IconThemeData(color: Colors.black),
backgroundColor: Colors.white,
title: Text(
"Add Game",
style: TextStyle(color: Colors.black),
),
actions: [
FlatButton(
onPressed: () {
if (formKey.currentState.validate()) {
formKey.currentState.save();
Fluttertoast.showToast(msg: "Game saved");
setState(() {
gameBloc.saveOrUpdate(_game, gameBloc.name,
gameBloc.description, "listGame");
});
Navigator.pushReplacementNamed(context, "home");
}
},
child: Text(
(StringUtils.isNullOrEmpty(_game.id)) ? "Add" : "Update",
style: TextStyle(color: HexColor(_game.color), fontSize: 20),
))
],
),
body: Form(
key: formKey,
child: Stack(children: <Widget>[
_createBackground(context, _game),
_createFormGame(context, _game, gameBloc)
]),
));
}
Widget _createBackground(BuildContext context, Game game) {
final size = MediaQuery.of(context).size;
final gradientTop = Container(
height: size.height, //* 0.4,
width: double.infinity,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: <Color>[HexColor(game.color), Colors.white])),
);
final circule = Container(
width: 100.0,
height: 100.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100.0),
color: Color.fromRGBO(255, 255, 255, 0.1)),
);
return Stack(
children: <Widget>[
gradientTop,
Positioned(
child: circule,
top: 90,
left: 50,
),
Positioned(
child: circule,
top: -40,
right: -30,
),
Container(
padding: EdgeInsets.only(top: 80),
child: Column(
children: <Widget>[
SizedBox(
height: 10.0,
width: double.infinity,
),
],
),
)
],
);
}
Widget _createFormGame(BuildContext context, Game game, GameBloc gameBloc) {
final size = MediaQuery.of(context).size;
return SingleChildScrollView(
child: Column(
children: <Widget>[
SafeArea(
child: Container(
height: 80.0,
)),
Container(
width: size.width * 0.85,
padding: EdgeInsets.symmetric(vertical: 50.0),
margin: EdgeInsets.symmetric(vertical: 30.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(5.0),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black26,
blurRadius: 3.0,
offset: Offset(0.0, 5.0),
spreadRadius: 3.0)
]),
child: Column(
children: <Widget>[
Text("Foto", style: TextStyle(fontSize: 20.0)),
SizedBox(
height: 50.0,
),
_createNameImput(gameBloc, game),
_createDescriptionImput(gameBloc, game),
Divider(
height: 30,
color: HexColor(game.color),
indent: 30,
endIndent: 20,
),
_createWasGameImput(gameBloc, game),
Divider(
height: 30,
color: HexColor(game.color),
indent: 30,
endIndent: 20,
),
_createToTheBacklogImput(gameBloc, game),
SizedBox(height: 60),
_createDeleteButton(gameBloc, game),
SizedBox(height: 60),
],
))
],
),
);
}
#override
void dispose() {
gameBloc?.dispose();
super.dispose();
}
Widget _createWasGameImput(GameBloc gameBloc, Game game) {
return StreamBuilder(
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: SwitchListTile(
activeColor: HexColor(game.color),
title: Text("Do you have it?"),
value: game.isBuyIt,
onChanged: (bool value) {
setState(() {
game.isBuyIt = value;
});
},
secondary: IconButton(
icon: Icon(Icons.shopping_cart),
onPressed: null,
color: HexColor(game.color),
),
));
},
);
}
Widget _createToTheBacklogImput(GameBloc gameBloc, Game game) {
return StreamBuilder(
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: SwitchListTile(
activeColor: HexColor(game.color),
title: Text("To the backlog?"),
value: game.isOnBacklog,
onChanged: (bool value) {
setState(() {
game.isOnBacklog = true;
});
},
secondary: IconButton(
icon: Icon(Icons.list),
onPressed: null,
color: HexColor(game.color),
),
));
},
);
}
Widget _createNameImput(GameBloc gamebloc, Game game) {
return Column(children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: TextFormField(
textCapitalization: TextCapitalization.sentences,
initialValue: game.name,
onSaved: (value) {
gameBloc.setName(value);
},
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: "Name",
icon: Icon(
Icons.games,
color: HexColor(game.color),
)),
),
),
Divider(
height: 30,
color: HexColor(game.color),
indent: 30,
endIndent: 20,
),
]);
}
Widget _createDescriptionImput(GameBloc gameBloc, Game game) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: TextFormField(
textCapitalization: TextCapitalization.sentences,
initialValue: game.description,
onSaved: (value) {
gameBloc.setDescription(value);
},
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: "Description",
icon: Icon(
Icons.description,
color: HexColor(game.color),
)),
),
);
}
Widget _createDeleteButton(GameBloc gameBloc, Game game) {
if (StringUtils.isNotNullOrEmpty(game.id)) {
return FlatButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text("Do you wan to remove the game"),
actions: <Widget>[
FlatButton(
onPressed: () {
setState(() {
gameBloc.remove(game, "listGame");
});
Navigator.pop(context);
Navigator.pop(context);
},
child: Text("Yes")),
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text("No"))
],
);
});
},
child: Text("Remove Game"));
} else {
return Container();
}
}
}
This is the bloc
class GameBloc extends Validators {
//Controller
final _allDataGames = BehaviorSubject<List<Game>>();
final _descriptionController = BehaviorSubject<String>();
final _nameController = BehaviorSubject<String>();
final _allMyListGamesByNameController = BehaviorSubject<List<Game>>();
//Services
GameService gameService = GameService();
//get Data from streams
Stream<List<Game>> get allGameData => _allDataGames.stream;
Stream<List<Game>> get allGameByNameList =>
_allMyListGamesByNameController.stream;
Stream<String> get getDescriptionStream =>
_descriptionController.stream.transform(validateDescription);
Stream<String> get getNameStream =>
_nameController.stream.transform(validName);
//Observable
Stream<bool> get validateDescriptionStream =>
Rx.combineLatest([getDescriptionStream], (description) => true);
Stream<bool> get validateNameStream =>
Rx.combineLatest([getNameStream], (name) => true);
//Set Stream
Function(String) get setDescription => _descriptionController.sink.add;
Function(String) get setName => _nameController.sink.add;
//Get Stream
//From repo
void allGames() async {
List<Game> games = await gameService.getAllDataGames();
_allDataGames.sink.add(games);
}
//From my setting
void allMyListGamesByName(String listName) async {
List<Game> games = await gameService.allMyListGamesByName(listName);
_allMyListGamesByNameController.sink.add(games);
}
void saveOrUpdate(
Game game, String name, String description, String listGame) {
game.name = name;
game.description = description;
if (StringUtils.isNullOrEmpty(game.id)) {
game.id = Uuid().v1();
gameService.add(game, listGame);
} else {
gameService.update(game);
}
}
void remove(Game game, String listGame) {
gameService.remove(game, listGame);
}
//Get Lastest stream value
String get name => _nameController.value;
String get description => _descriptionController.value;
dispose() {
_descriptionController?.close();
_allMyListGamesByNameController?.close();
_allDataGames?.close();
_nameController?.close();
}
}
The provider:
class Provider extends InheritedWidget {
static Provider _imstance;
final _gameBloc = GameBloc();
factory Provider({Key key, Widget child}) {
if (_imstance == null) {
_imstance = new Provider._internal(key: key, child: child);
}
return _imstance;
}
Provider._internal({Key key, Widget child}) : super(key: key, child: child);
static GameBloc gameBloc(BuildContext context) {
return (context.inheritFromWidgetOfExactType(Provider) as Provider)
._gameBloc;
}
#override
bool updateShouldNotify(InheritedWidget oldWidget) {
return true;
}
}
The error is:
════════ Exception caught by gesture ═══════════════════════════════════════════
Bad state: Cannot add new events after calling close
When I evaluate formKey.currentState.save(); I got:
formKey.currentState.save()
Unhandled exception:
Bad state: Cannot add new events after calling close
#0 _BroadcastStreamController.add (dart:async/broadcast_stream_controller.dart:249:24)
#1 Subject._add (package:rxdart/src/subjects/subject.dart:141:17)
#2 Subject.add (package:rxdart/src/subjects/subject.dart:135:5)
#3 _StreamSinkWrapper.add (package:rxdart/src/subjects/subject.dart:167:13)
I was reading about this error, it mention the error is on Bloc singleston scope or dispose method.
What is happen?
When you navigate to home with Navigator.pushReplacementNamed(context, "home"), the _DetailGamePage<State> is being disposed, calling gameBloc?.dispose. This leaves _gameBloc instantiated with all streams closed.
As you are using a Singleton Provider, when you navigate back to DetailGamePage, your save is trying to write to the closed streams.
What you need to do is move the closure of the streams farther up the widget tree so as not to close them before you are done with them, perhaps at the app level OR re-instantiate _gameBloc if the streams are closed, loading the data from the repo again.
I have encountered a problem which I cannot move between pages when I click on the icons, but I am setting the default page is the home page and when the application is running it only appears and I cannot move to other pages using the navigation bar.
Navigation Bar File:
class NavigationBar extends StatefulWidget {
#override
_NavigationBarState createState() => _NavigationBarState();
}
class _NavigationBarState extends State<NavigationBar> {
int pageIndex = 0;
final Search _search = Search();
final UserNoti _userNoti = UserNoti();
final HomePage _homePage = HomePage();
final MyProfile _myProfile = MyProfile();
final MyMajor _mymajor = MyMajor();
Widget _showPage = new HomePage();
Widget _pageChooser(int page)
{
switch (page)
{
case 0:
return _search;
break;
case 1:
return _userNoti;
break;
case 2:
return _homePage;
break;
case 3:
return _myProfile;
break;
case 4:
return _mymajor;
break;
default:
return _homePage;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: Container(
decoration: new BoxDecoration(
// borderRadius: BorderRadius.circular(28.0),
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [Colors.deepPurple, Colors.purple]),
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 25.0, // soften the shadow
spreadRadius: -10.0, //extend the shadow
)
],
),
child: CurvedNavigationBar(
index: 2,
color: Color.fromRGBO(58, 66, 86, 1.0),
buttonBackgroundColor: Colors.purple.withOpacity(0.6),
backgroundColor: Colors.transparent,
animationDuration: Duration(milliseconds: 200),
animationCurve: Curves.bounceInOut,
items: <Widget>[
Icon(Icons.search, size: 32, color: Colors.white,),
Icon(Icons.notifications_active, size: 32, color: Colors.white,),
Icon(Icons.home, size: 40, color: Colors.white,),
Icon(Icons.chat, size: 32, color: Colors.white,),
Icon(Icons.school, size: 32, color: Colors.white,),
],
onTap: (int tappedPage){
_showPage = _pageChooser(tappedPage);
},),
),
body: Container(
color: Colors.transparent,
child: Center(
child: _showPage,
),
));
}
}
Now I will put one code for one of those pages for example User Notification Page:
class UserNoti extends StatefulWidget {
final String myuid,majname,uniname;
UserNoti({Key key, this.myuid,this.majname,this.uniname}) : super(key: key);
#override
_UserNotiState createState() => _UserNotiState();
}
class _UserNotiState extends State<UserNoti> {
String myapplang = null;
var my_uid;
var myDoc;
bool loading = false;
#override
void initState() {
super.initState();
checkLang();
}
#override
Widget build(BuildContext context) {
return loading
? LoadingMain()
: Directionality(
textDirection: myapplang == 'ar' ? TextDirection.rtl : TextDirection.ltr,
child: Scaffold(
backgroundColor: Color.fromRGBO(58, 66, 86, 1.0),
appBar: GradientAppBar(
backgroundColorStart: Colors.deepPurple,
backgroundColorEnd: Colors.purple,
title: Text(
myapplang == 'ar' ? "الإشعارات" : "Notifications" ,
style: TextStyle(fontSize: 16,fontWeight: FontWeight.bold,fontFamily: myapplang == 'ar' ? 'Tajawal' : 'BalooDa2' ),),
),
body: ListView(
children: <Widget>[
Column(
children: <Widget>[
SizedBox(height: 30.0,),
Container(
width: MediaQuery.of(context).size.width*0.85,
child: Stack(
children: <Widget>[
StreamBuilder(
stream: Firestore.instance.collection('users').document(myDoc).collection('Notifications').orderBy('date',descending: true).snapshots(),
builder: (context, snapshot){
if(!snapshot.hasData)
{
const Text('Loading...');
}
else{
return ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index)
{
DocumentSnapshot mypost = snapshot.data.documents[index];
return Stack(
children: <Widget>[
FadeAnimation(0.2,Column(
children: <Widget>[
Container(
decoration: BoxDecoration(borderRadius:
BorderRadius.circular(15.0),color: Color.fromRGBO(64, 75, 96, .9),
boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 30.0, // soften the shadow
spreadRadius: -25.0, //extend the shadow
)
],),
child:InkWell(
onTap: () {
// setState(() => loading = true);
if(mypost['type'] == 'comment'){
if(mypost['location'] == 'Majors'){
Firestore.instance
.document('/Majors/${widget.majname}/Posts/${mypost['pdoc']}')
.get()
.then((val) {
if(val.data['pid'] != null && val.data['text'] != null && val.data['uid'] != null && val.data['uimg'] != null && val.data['uname'] != null && val.data['uname'] != null)
{
Navigator.push(context, new MaterialPageRoute(builder: (context) => new Comments(pid: val.data['pid'], text: val.data['text'],img: val.data['img'],uid: val.data['uid'], uimg: val.data['uimg'],uname: val.data['uname'],majname: widget.majname,pdoc:mypost['pdoc'] )));
}
else {
showWraning(context,myapplang == 'ar' ? "حدثت مشكلة، قد يكون الإشعار محذوف أو مشكلة بالإنترنت، حاول لاحقًا." : "There is a problem, maybe notification has been deleted or problem with internet connection, try again later.");
}
/*
*/
}).catchError((e) {
print(e);
});
}
else {
Firestore.instance
.document('/Universities/${widget.uniname}/Posts/${mypost['pdoc']}')
.get()
.then((val) {
if(val.data['pid'] != null && val.data['text'] != null && val.data['uid'] != null && val.data['uimg'] != null && val.data['uname'] != null && val.data['uname'] != null)
{
Navigator.push(context, new MaterialPageRoute(builder: (context) => new U_Comments(pid: val.data['pid'], text: val.data['text'],img: val.data['img'],uid: val.data['uid'], uimg: val.data['uimg'],uname: val.data['uname'],uniname: widget.uniname,pdoc:mypost['pdoc'] )));
}
else {
showWraning(context,myapplang == 'ar' ? "حدثت مشكلة، قد يكون الإشعار محذوف أو مشكلة بالإنترنت، حاول لاحقًا." : "There is a problem, maybe notification has been deleted or problem with internet connection, try again later.");
}
/*
*/
}).catchError((e) {
print(e);
});
}
}
else {
print('not comment!');
}
},
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
leading: ClipRRect(
borderRadius: BorderRadius.circular(35.0),
child: Icon(Icons.insert_comment,color: Colors.deepPurple,size: 40,),
),
title: Column(
children: <Widget>[
Text(
"${mypost['text']}",
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold,fontFamily: 'BalooDa2'),
textAlign: TextAlign.left,
),
SizedBox(height: 10.0,),
],
),
// subtitle: Text("Intermediate", style: TextStyle(color: Colors.white)),
trailing:
Icon(Icons.keyboard_arrow_right, color: Colors.white, size: 30.0)),
),
),
SizedBox(height: 20.0,),
],
),),
],
);
}
);
}
return Container(
height: 0.0,
width: 0.0,
);
},
),
],
),
),
],
),
],
),
),
);
}
I removed a few codes that had no need.
Note when running the application the active page is Homepage()
Right now you are setting your _showPage variable with a new page, but not in a way that will force your app to re-render the widget tree. Try using SetState in your tap function to do this.
onTap: (int tappedPage){
setState(() {
_showPage = _pageChooser(tappedPage);
});
}),