LateError was thrown building Obx(has builder, dirty, state: _ObxState#17527): LateInitializationError: Field 'products' has not been initialized - flutter-getx

I am using GetX. I need to display the data from database one the application open. But i get the Exception.
The following LateError was thrown building Obx(has builder, dirty,
state: _ObxState#17527): LateInitializationError: Field 'products' has
not been initialized.
Although, I have initialize the late variable inside onInit.
The Controller Code Is:
class HomeController extends GetxController {
HomeController({required this.homeRepository});
final IHomeRepository homeRepository;
late Rx<Either<FireStoreServerFailures, List<Product>>> products; //=
#override
void onInit() async {
products.value= await fetchProductsFromDB();
super.onInit();
}
Future<Either<FireStoreServerFailures, List<Product>>> fetchProductsFromDB() async {
var _completer=Completer<Either<FireStoreServerFailures, List<Product>>>();
homeRepository.fetchProducts().listen((event) {
_completer.complete(event);
});
return await _completer.future;
}
}
Home Page Where I Am Used The Late Variable's Code Is:
Obx(
() => GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 2 / 2,
),
// TODO Deal with errors
itemCount: controller.products.value.getOrElse(() => []).length,
itemBuilder: (context, index) {
return controller.products.value.fold(
(l) => Container(
height: double.infinity,
width: double.infinity,
color: Colors.red,
child: Text("Error ${l.msg}"),
),
(r) => Card(
elevation: 2.0,
color: Colors.amber,
child: InkWell(
onTap: () async =>
await controller.goToMoreDetails(),
child: Stack(
fit: StackFit.expand,
clipBehavior: Clip.hardEdge,
children: [
Image.network(
controller.products.value.fold(
(l) => "",
(r) => r[index]
.pickedImages
.getOrCrash()[0]),
fit: BoxFit.fill,
height: 200,
width: 200,
),
OverflowBox(
minHeight: 30,
alignment: Alignment.bottomCenter,
child: Container(
color: Colors.black.withOpacity(0.7),
height: 30,
width: double.infinity,
child: Center(
child: Text(
"${controller.products.value.fold((l) => "", (r) => r[index].price.getOrCrash())}",
style: const TextStyle(
color: Colors.white,
),
),
),
),
),
],
),
),
)) ;
}),
);

You declared late Rx<Either<FireStoreServerFailures, List<Product>>> products; but did not initialise products anywhere. But on your onInit method you tried to access .value on products which is not yet initialised. Setting products.value is not initialising products. Also, your Obx widget tries to access the products field which is not initialised by the time it tries to access.
What you should do is:
final products = Rxn<Either<FireStoreServerFailures, List<Product>>>();
And in your UI:
Obx(
() => controller.products.value == null? CircularProgressIndicator() : GridView.builder(......

Related

Getting type 'Null' is not a subtype of type 'BuildContext' while trying to push data from one flutter screen to another

Here's widget.dart
Widget wallpapersList({required List<WallpaperModel> wallpapers, context}) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 16),
child: GridView.count(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
crossAxisCount: 2,
childAspectRatio: 0.6,
mainAxisSpacing: 6.0,
crossAxisSpacing: 6.0,
children: wallpapers.map((wallpaper) {
return GridTile(
child: GestureDetector(
onTap: () {
print(wallpaper.src!.portrait); // printing successfully
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ImageView(wallpaper.src!.portrait),
));
},
child: Hero(
tag: wallpaper.src?.portrait ??
"https://www.pexels.com/photo/brown-grass-field-near-body-of-water-under-white-clouds-14491698/",
child: Container(
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Image.network(
wallpaper.src?.portrait ??
"https://www.pexels.com/photo/brown-grass-field-near-body-of-water-under-white-clouds-14491698/",
fit: BoxFit.cover,
),
),
),
),
));
}).toList(),
),
);
}
Here's image_view.dart
class ImageView extends StatefulWidget {
String imgUrl;
ImageView(this.imgUrl);
#override
State<ImageView> createState() => _ImageViewState();
}
class _ImageViewState extends State<ImageView> {
#override
Widget build(BuildContext context) {
print(widget.imgUrl);
return Scaffold(
body: Stack(
children: [
Hero(
tag: widget.imgUrl,
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Image.network(
widget.imgUrl,
fit: BoxFit.cover,
)),
),
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Color(0x36FFFFFF), Color(0x0FFFFFFF)])),
child: Column(children: [Text("Download backpaper")]),
),
Text(
"Back",
style: TextStyle(color: Colors.white),
)
],
),
);
}
}
Getting this error:
════════ Exception caught by gesture ═══════════════════════════════════════════
type 'Null' is not a subtype of type 'BuildContext'
════════════════════════════════════════════════════════════════════════════════
I searched the web & it mentioned that the value we are trying to push to another screen might be null but that's not the case here. What could be causing this error?
It must mean that you are calling wallpapersList without giving it a context.
Note that the signature is
wallpapersList({required List<WallpaperModel> wallpapers, context})
I strongly suggest to change it to
wallpapersList({required List<WallpaperModel> wallpapers, required BuildContext context})
Giving it an explicit type and also making it required. It would probably then point you exactly to the spot where you are using it without context.
This context must be given because it's the context used here:
Navigator.push(
context,
The error you are having is about this context being null, which is the context parameter of wallpapersList

Error: type 'String' is not a subtype of type 'int' of 'index'

(Edit: I have 3 files
main , trending and text. Let me show them.) I am showing you 2 of them here - where the mainly important portions are.
I think the problem is within 'trending' page where trending[index] is not accepting as String. The api is from tmdb which is a list of maps and has 'poster path'. Donot know what the problem is.
Main =>
class _HomeState extends State<Home> {
#override
void initState() {
loadmovies();
super.initState();
}
List trendingmovies = [];
List topratedmovieslist = [];
List tvlist = [];
final String apikey = '974ecf335095695927b80cb92fbe6200';
final readaccesstoken = 'eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI5NzRlY2YzMzUwOTU2OTU5MjdiODBjYjkyZmJlNjIwMCIsInN1YiI6IjYzMzAwZmJkYWJkYWZjMDA3Y2Q5OWRkZCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.LZkUXKdyN5YCWJcfV6wYLVejGvYp22BzAQowBUWe5Ys';
loadmovies() async{
final tmdb = TMDB(
ApiKeys(apikey, readaccesstoken),
logConfig: const ConfigLogger(
showLogs: true,//must be true than only all other logs will be shown
showErrorLogs: true,
),);
Map result = await tmdb.v3.trending.getTrending();
Map topratedmovies = await tmdb.v3.movies.getTopRated();
Map tv = await tmdb.v3.tv.getTopRated();
setState(() {
trendingmovies = result['results'];
topratedmovieslist = topratedmovies['results'];
tvlist = tv['results'];
});
print(trendingmovies);
print(topratedmovieslist);
print(tvlist);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: const modified_text(text:'Film Library', color: Colors.white70, size: 20,
),
backgroundColor: Colors.transparent,
),
body: ListView(
children: [
TrendingMovies(trending: [trendingmovies],)
],
),
);
}
}
Edit: This is the trending file. I have noted that initialized list doesn't contain poster_path like with tmdb but the main file has extracted it to a list.
Trending ->
import 'package:film_library/utils/text.dart';
import 'package:flutter/material.dart';
class TrendingMovies extends StatelessWidget {
const TrendingMovies({Key? key, required this.trending}) : super(key: key);
final List trending;
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const modified_text (text:'Trending Movies', size: 30, color: Colors.white,),
SizedBox(height: 300,
child: ListView.builder(itemCount: trending.length,
scrollDirection: Axis.horizontal,
itemBuilder: (context,index){
return InkWell(
onTap: (){
},
child: SizedBox(
width: 140,
child: Column(
children: [
Container(
height: 200,
decoration: BoxDecoration(
image: DecorationImage(image: NetworkImage(
'http://image.tmdb.org/t/p/w500' + trending[index] ['poster_path']))
)
),
modified_text(text: trending[index]['title'], color: Colors.brown, size: 20,)
],
),
),
);
}),
)
],
),
);
}
}
Edit: There is text file as well but it is nothing but just used as some Font class. I have created model class like - List trending but it too says that - Error: A value of type 'List' can't be assigned to a variable of type 'Trending Movies', and shows error on same poster_path. Maybe the syntax is wrong with calling it as #jacksparrow, did while it doesnt show the error here with [index] ['poster_path'] but that one of the title.
I would suggest that please create model class from json response of your Trending movies API and then create list of trending movies using that model like as below :
List<TrendingMovies> trending ;
It is better to fetch data from API by making model class and fromJson method of that particular json response. It makes more easier to understand use data in it.
import 'package:film_library/utils/text.dart';
import 'package:flutter/material.dart';
class TrendingMovies extends StatelessWidget {
const TrendingMovies({Key? key, required this.trending}) : super(key: key);
List<TrendingMovies> trendingMoviesList ;
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const modified_text (text:'Trending Movies', size: 30, color: Colors.white,),
SizedBox(
height: 300,
child: ListView.builder(
itemCount: trending.length,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
final trendingMovie= trendingMoviesList[index];
return InkWell(
onTap: () {},
child: SizedBox(
width: 140,
child: Column(
children: [
Container(
height: 200,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
"http://image.tmdb.org/t/p/w500${trendingMovie.poster_path}"),
),
),
),
modified_text(text: trendingMovie.title, color: Colors.brown, size: 20,)
],
),
),
);
}),
)
],
),
);
}
}

How to wait for a request to complete using ObservableFuture?

When I transition to a screen where I get a list of information via an API, it initially gives an error:
_CastError (Null check operator used on a null value)
and only after loading the information, the screen is displayed correctly.
I am declaring the variables like this:
#observable
ObservableFuture<Model?>? myKeys;
#action
getKeys() {
myKeys = repository.getKeys().asObservable();
}
How can I enter the page only after loading the information?
In button action I tried this but to no avail!
await Future.wait([controller.controller.getKeys()]);
Modular.to.pushNamed('/home');
This is the page where the error occurs momentarily, but a short time later, that is, when the api call occurs, the data appears on the screen.
class MyKeyPage extends StatefulWidget {
const MyKeyPage({Key? key}) : super(key: key);
#override
State<MyKeyPage> createState() => _MyKeyPageState();
}
class _MyKeyPageState
extends ModularState<MyKeyPage, KeyController> {
KeyController controller = Modular.get<KeyController>();
Widget countKeys() {
return FutureBuilder(
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
final count =
controller.myKeys?.value?.data!.length.toString();
if (snapshot.connectionState == ConnectionState.none &&
!snapshot.hasData) {
return Text('..');
}
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: 1,
itemBuilder: (context, index) {
return Text(count.toString() + '/5');
});
},
future: controller.getCountKeys(),
);
}
#override
Widget build(BuildContext context) {
Size _size = MediaQuery.of(context).size;
return controller.getCountKeys() != "0"
? TesteScaffold(
removeHorizontalPadding: true,
onBackPressed: () => Modular.to.navigate('/exit'),
leadingIcon: ConstantsIcons.trn_arrow_left,
title: '',
child: Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.only(left: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Keys',
style: kHeaderH3Bold.copyWith(
color: kBluePrimaryTrinus,
),
),
countKeys(),
],
),
),
),
body: Observer(builder: (_) {
return Padding(
padding: const EdgeInsets.only(bottom: 81),
child: Container(
child: ListView.builder(
padding: EdgeInsets.only(
left: 12.0,
top: 2.0,
right: 12.0,
),
itemCount:
controller.myKeys?.value?.data!.length,
itemBuilder: (context, index) {
var typeKey = controller
.myKeys?.value?.data?[index].type
.toString();
var id =
controller.myKeys?.value?.data?[index].id;
final value = controller
.myKeys?.value?.data?[index].value
.toString();
return GestureDetector(
onTap: () {
.
.
},
child: CardMeyKeys(
typeKey: typeKey,
value: value!.length > 25
? value.substring(0, 25) + '...'
: value,
myKeys: pixController
.minhasChaves?.value?.data?[index].type
.toString(),
),
);
},
),
),
);
}),
bottomSheet: ....
)
: TesteScaffold(
removeHorizontalPadding: true,
onBackPressed: () => Modular.to.navigate('/exit'),
leadingIcon: ConstantsIcons.trn_arrow_left,
title: '',
child: Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.only(left: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'...',
style: kHeaderH3Bold.copyWith(
color: kBluePrimaryTrinus,
),
),
],
),
),
),
body: Padding(
padding: const EdgeInsets.only(bottom: 81),
child: Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(
'assets/images/Box.png',
fit: BoxFit.cover,
width: 82.75,
height: 80.91,
),
SizedBox(
height: 10,
),
],
),
), //Center
),
),
bottomSheet: ...
);
}
List<ReactionDisposer> disposers = [];
#override
void initState() {
super.initState();
controller.getKeys();
}
#override
void dispose() {
disposers.forEach((toDispose) => toDispose());
super.dispose();
}
}
Initially the error occurs in this block
value: value!.length > 25
? value.substring(0, 25) + '...'
: value,
_CastError (Null check operator used on a null value)
I appreciate if anyone can help me handle ObservableFuture correctly!
You need to call the "future" adding
Future.wait
(the return type of getKeys) keys=await Future.wait([
controller.getKeys();
]);
The problem is your getKeys function isn't returning anything, so there's nothing for your code to await. You need to return a future in order to await it.
Future<Model?> getKeys() {
myKeys = repository.getKeys().asObservable();
return myKeys!; // Presumably this isn't null anymore by this point.
}
...
await controller.controller.getKeys();
Modular.to.pushNamed('/home');

How to update the ui when my list gets filled with data GetX Flutter

Im trying to show a listView.builder inside a AlertDialog, and Im filling the its list by calling a function everytime the button to open the AlertDialog is pressed but the problem is that the ui doesn’t update when the list is filled with the data, I'm using getX and I'm very new to it, can someone show me what I'm doing wrong?
I'm using the GetX builder:
GetX<DashboardController>(
init: Get.put<DashboardController>(DashboardController()),
builder: (DashboardController dashboardController) {
return GridView.builder(
My Get.dialog function:
return GestureDetector(
onTap: () {
// this is where I'm filling the list
dashboardController
.callEmployeeCheckInOutList(_employeeModel.id);
Get.dialog(
AlertDialog(
contentPadding: EdgeInsets.zero,
content: SizedBox(
height: size.height * 0.55,
width: size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
EmployeeProfileWidget(
size: size,
profileBackgroudPath: profileBackgroudPath,
employeeModel: _employeeModel,
),
// this is where my listview.builder resides
EmployeeActivityWidget(
closeCrossPath: closeCrossPath,
employeeCheckInOutList:
_employeeCheckInOutList,
employeeModel: _employeeModel,
onTap: () {},
),
],
),
),
),
);
},
My listview.builder:
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 32.0,
right: 50.0,
),
child: ListView.builder(
itemCount: employeeCheckInOutList.length,
shrinkWrap: true,
itemBuilder: (context, index) {
final _checkInOutModel = employeeCheckInOutList[index];
return SizedBox(
height: 120,
child: TimelineTile(
beforeLineStyle: const LineStyle(
color: Color(0xffa5affb),
),
My Controller:
Rx<List<CheckInOutModel>> _employeeCheckInOutList =
Rx<List<CheckInOutModel>>([]);
List<CheckInOutModel> get employeeCheckInOutList =>
_employeeCheckInOutList.value;
Future<void> callEmployeeCheckInOutList(String id) async {
_employeeCheckInOutList =
await EmployeeService.employeeCheckInOutFuture(docId: id);
update();
}
Use .assignAll method on the RxList to trigger UI update:
Future<void> callEmployeeCheckInOutList(String id) async {
final result = await EmployeeService.employeeCheckInOutFuture(docId: id);
_employeeCheckInOutList.assignAll(result);
}
And you don't need to call update() when using Rx.
I already faced same issue.
Solution:
Simply use again GetX<Controller> inside AlertDialog
like
GetX<DashboardController>(
init: Get.put<DashboardController>(DashboardController()),
builder: (DashboardController dashboardController) {
return GridView.builder(
.....
Get.dialog(
AlertDialog(
contentPadding: EdgeInsets.zero,
content: GetX<DashboardController>(
init: Get.put<DashboardController>(DashboardController()),
builder: (DashboardController dashboardController) {
SizedBox(

Another exception was thrown: setState() or markNeedsBuild() called during build Error in flutter

Im new to flutter and working on an ecommerce flutter app. When im trying to navigate to search screen its giving some error. Please find the below codes for your reference and help to resolve.
Error :
The following assertion was thrown while dispatching notifications for SearchProvider:
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope<SearchProvider?> widget cannot be marked as needing to build because
the framework is already in the process of building widgets. A widget can be marked as needing to
be built during the build phase only if one of its ancestors is currently building. This exception
is allowed because the framework builds parent widgets before children, which means a dirty
descendant will always be built. Otherwise, the framework might not visit this widget during this
build phase.
The widget on which setState() or markNeedsBuild() was called was:
_InheritedProviderScope<SearchProvider?>
The widget which was currently being built when the offending call was made was:
SearchScreen
Codes
Search Screen
class SearchScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
Provider.of<SearchProvider>(context, listen: false).cleanSearchProduct();
Provider.of<SearchProvider>(context, listen: false).initHistoryList();
return Scaffold(
backgroundColor: ColorResources.getIconBg(context),
resizeToAvoidBottomInset: true,
body: Column(
children: [
// for tool bar
SearchWidget(
hintText: getTranslated('SEARCH_HINT', context),
onSubmit: (String text) {
Provider.of<SearchProvider>(context, listen: false)
.searchProduct(text, context);
Provider.of<SearchProvider>(context, listen: false)
.saveSearchAddress(text);
},
onClearPressed: () {
Provider.of<SearchProvider>(context, listen: false)
.cleanSearchProduct();
},
),
Consumer<SearchProvider>(
builder: (context, searchProvider, child) {
return !searchProvider.isClear
? searchProvider.searchProductList != null
? searchProvider.searchProductList.length > 0
? Expanded(
child: SearchProductWidget(
products: searchProvider.searchProductList,
isViewScrollable: true))
: Expanded(
child:
NoInternetOrDataScreen(isNoInternet: false))
: Expanded(
child: ProductShimmer(
isHomePage: false,
isEnabled: Provider.of<SearchProvider>(context)
.searchProductList ==
null))
: Expanded(
flex: 4,
child: Container(
padding:
EdgeInsets.all(Dimensions.PADDING_SIZE_DEFAULT),
child: Stack(
clipBehavior: Clip.none,
children: [
Consumer<SearchProvider>(
builder: (context, searchProvider, child) =>
StaggeredGridView.countBuilder(
crossAxisCount: 3,
physics: NeverScrollableScrollPhysics(),
itemCount: searchProvider.historyList.length,
itemBuilder: (context, index) => Container(
alignment: Alignment.center,
child: InkWell(
onTap: () {
Provider.of<SearchProvider>(context,
listen: false)
.searchProduct(
searchProvider
.historyList[index],
context);
},
borderRadius: BorderRadius.circular(20),
child: Container(
padding: EdgeInsets.only(
left: 10,
right: 10,
top: 2,
bottom: 2),
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(16),
color: ColorResources.getGrey(
context)),
width: double.infinity,
child: Center(
child: Text(
Provider.of<SearchProvider>(context,
listen: false)
.historyList[index] ??
"",
style: titilliumItalic.copyWith(
fontSize:
Dimensions.FONT_SIZE_SMALL),
),
),
),
)),
staggeredTileBuilder: (int index) =>
new StaggeredTile.fit(1),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
),
),
Positioned(
top: -5,
left: 0,
right: 0,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(getTranslated('SEARCH_HISTORY', context),
style: robotoBold),
InkWell(
borderRadius: BorderRadius.circular(10),
onTap: () {
Provider.of<SearchProvider>(context,
listen: false)
.clearSearchAddress();
},
child: Container(
padding: EdgeInsets.all(5),
child: Text(
getTranslated('REMOVE', context),
style: titilliumRegular.copyWith(
fontSize:
Dimensions.FONT_SIZE_SMALL,
color: Theme.of(context)
.primaryColor),
)))
],
),
),
],
),
),
);
},
),
],
),
);
}
}
Providers
void initHistoryList() {
_historyList = [];
_historyList.addAll(searchRepo.getSearchAddress());
notifyListeners();
}
void cleanSearchProduct() {
_searchProductList = [];
_isClear = true;
_searchText = '';
notifyListeners();
}
Try to use initial function calling in initState instead of build function
#override
void initState() {
WidgetsBinding.instance!.addPostFrameCallback((_) {
Provider.of<SearchProvider>(context, listen: false).cleanSearchProduct();
Provider.of<SearchProvider>(context, listen: false).initHistoryList();
});
super.initState();
}