CheckboxListTile has null value using Bloc and Stream on Flutter - flutter

I am creating register form on my flutter app (version 1.17.4). I am using CheckboxListTile in order to user accept the terms. This widget is validated by bloc and stream
CheckboxListTile
Widget _createAcceptConditions(LoginBloc bloc) {
return StreamBuilder(
stream: bloc.getAcceptCondition,
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Container(
child: Container(
padding: EdgeInsets.only(left: 5, top: 10),
child: CheckboxListTile(
title: Text("I accept the terms"),
value: bloc.acceptCondition,
activeColor: Colors.deepPurple,
controlAffinity: ListTileControlAffinity.leading,
onChanged: (value) {
bloc.setAcceptCondition(value);
})),
);
},
);
}
LoginBloc class
final _acceptCondition = BehaviorSubject<bool>();
Stream<bool> get getAcceptCondition =>
_acceptCondition.stream.transform(validAcceptCondition);
//Setter
Function(bool) get setAcceptCondition => _acceptCondition.sink.add;
//Getter
bool get acceptCondition => _acceptCondition.value;
This is the validator
final validAcceptCondition =
StreamTransformer<bool, bool>.fromHandlers(handleData: (accept, sink) {
accept ? sink.add(accept) : sink.addError("You must accept the conditions");
});
When I restart the app an try to register I got
════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building StreamBuilder<bool>(dirty, state: _StreamBuilderBaseState<bool, AsyncSnapshot<bool>>#8f6de):
'package:flutter/src/material/checkbox_list_tile.dart': Failed assertion: line 269 pos 15: 'value != null': is not true.
The relevant error-causing widget was
StreamBuilder<bool>
package:neighbour_mobile/…/pages/register_page.dart:205
When the exception was thrown, this was the stack
#2 new CheckboxListTile
package:flutter/…/material/checkbox_list_tile.dart:269
#3 RegisterPage._createAcceptConditions.<anonymous closure>
package:neighbour_mobile/…/pages/register_page.dart:211
#4 StreamBuilder.build
package:flutter/…/widgets/async.dart:509
#5 _StreamBuilderBaseState.build
package:flutter/…/widgets/async.dart:127
#6 StatefulElement.build
package:flutter/…/widgets/framework.dart:4619
...
It seem the bloc is waiting for any user acction in order to put true or false into CheckboxListTile, how ever the default value is null

The value in the CheckBox cannot be null, and when you create a BehaviorSubject or a Stream they doesn't have any data. So you can work with the snapshot value and defining a initialData property in you StreamBuilder to initialize a default value when the Stream is created, try the next:
Widget _createAcceptConditions(LoginBloc bloc) {
return StreamBuilder(
stream: bloc.getAcceptCondition,
// Add a initialData
initialData: false,
builder: (BuildContext context, AsyncSnapshot snapshot) {
// In this point you can validate the snapshot to show the error you are getting
/**if(snapshot.hasError){
// Do or show something, for example a Snackbar
}*/
return Container(
child: Container(
padding: EdgeInsets.only(left: 5, top: 10),
child: CheckboxListTile(
title: Text("I accept the terms"),
// You don't need to use the acceptCondition in this section, because you have the value in the snapshot
// value: bloc.acceptCondition,
// In this part the first time the snapshot will be false
value: snapshot.hasData && snapshot.data ? true : false,
activeColor: Colors.deepPurple,
controlAffinity: ListTileControlAffinity.leading,
onChanged: (value) {
bloc.setAcceptCondition(value);
},
),
),
);
},
);
}
Hope it helps.

Related

How to Obx in RxStatus.success() function

I use CheckboxListTile to input checkbox.
this is a function to retrieve a list of data from firestore
void initDoctorCategory() {
DoctorCategoryService().getListDoctorCategory().then((doctorCategory) {
change(doctorCategory, status: RxStatus.success());
});
}
And this is my widget. i cant using obx for update value in doctorCategory[index].value:
body: controller.obx(
(doctorCategory) => Container(
child: ListView.builder(
itemCount: doctorCategory!.length,
itemBuilder: (BuildContext context, int index) {
return Obx(() => CheckboxListTile(
title: Text(doctorCategory[index].categoryName!),
value: doctorCategory[index].value,
onChanged: (value) {
doctorCategory[index].value = value!;
// Get.back();
},
));
},
),
),
),
Im get the error:
[Get] the improper use of a GetX has been detected.
You should only use GetX or Obx for the specific widget that will be updated.
If you are seeing this error, you probably did not insert any observable variables into GetX/Obx
or insert them outside the scope that GetX considers suitable for an update
(example: GetX => HeavyWidget => variableObservable).
If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
"""
How to solved this?
How to using Getx in RxStatus.success()?
Try this:
body: Container(
child: Obx(() {
return ListView.builder(
itemCount: doctorCategory!.length,
itemBuilder: (BuildContext context, int index) {
return CheckboxListTile(
title: Text(doctorCategory[index].categoryName!),
value: doctorCategory[index].value,
onChanged: (value) {
doctorCategory[index].value = value!;
},
);
},
);
}),
),
Be sure that you have Get.put or Get.lazyPut of your controller

Flutter QR how to pass QR data to the next screen

How do I make it so when the user scan a QR code, the result will then be passed to the next screen.
Here is my code so far,
Widget build(BuildContext context) => SafeArea(
child: Scaffold(
body: Stack(
alignment: Alignment.center,
children: <Widget>[
buildQrView(context),
Positioned(top: 10, child: buildControlButtons()),
Positioned(bottom: 30, child: buildResult()),
],
),
),
The buildResult is this
Widget buildResult() => Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8), color: Colors.white24),
child: Text(
barcode != null ? _dataFetch() : 'Scan a code!',
maxLines: 3,
),
Then the function _dataFetch is as below
_dataFetch() async {
if (barcode == null) {
print('error');
} else {
var route = new MaterialPageRoute(
builder: (BuildContext context) =>
new TransferProcessQR(
value: PassdataQR(
email: barcode!.code.toString(),
)
)
);
Navigator.of(context).push(route);
}
I have another class for PassdataQR but its pretty self explanatory. With this code everytime I run it will give me an error
The following _TypeError was thrown building QRScanPage(dirty, dependencies: [MediaQuery], state: _QRScanPageState#720ae):
type 'Future' is not a subtype of type 'String'
and the Navigator functions will be messed up.
Is there another approach I can do, so after a QR code is scanned, the result will be passed to the next screen without errors?
It seems to me that your _dataFetch method returns a futureand in your buildResult method you're using it like so:
Text(
barcode != null ? _dataFetch() : 'Scan a code!',
maxLines: 3,
)
You can use a futurebuilder to retrieve the async data:
Widget buildResult() => Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8), color: Colors.white24),
child: FutureBuilder<string>(
future: _dataFetch,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.HasData) {
return Text(snapshot.data, maxLines: 3);
} else return Text('Scan a code!', maxLines: 3);
},
),
);
According to your repository you could just modify line 150:
controller.scannedDataStream
.listen((barcode) => {
setState(() => this.barcode = barcode));
Get.to(new TransferProcessQR(
value: PassdataQR(
email: barcode!.code.toString(),
)
));
}
Notice that in order for this to work you'll have to use the Get Package Route Management to navigate to another page. That's because you don't have access to the build context in this code snipped. Normally you would call Navigator.of(context).push(...) but that's not possible without a build context.

How to wrap navigation with blocprovider.value which inside a bloc listener

BlocConsumer<VehicleCubit, VehicleState>(
listener: (context, state) {
if (state is VehicleLoaded) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) {
final registrationNumber =
controller.text.trim().toUpperCase();
return BlocProvider.value(
value: BlocProvider.of<VehicleCubit>(context),
child: VehicleDetailsScreen(
registrationNumber: registrationNumber,
),
);
},
),
);
}
},
// TODO: Fix routing
builder: (context, state) {
if (state is VehicleLoading) {
return Center(
child: CircularProgressIndicator(
valueColor:
new AlwaysStoppedAnimation<Color>(brandColor),
),
);
}
if (state is VehicleNotLoaded) {
toast('Unable to fetch vehicle, retry..');
}
return ElevatedButton(
onPressed: () {
FocusScope.of(context).unfocus();
if (controller.text.trim().isEmpty) return;
final registrationNumber =
controller.text.trim().toUpperCase();
BlocProvider.of<VehicleCubit>(context)
.fetchVehicle(registrationNumber, context);
},
child: Text(
'Proceed',
style: TextStyle(fontSize: 16),
),
style: ButtonStyle(
shape: MaterialStateProperty.resolveWith(
(states) => RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
)),
backgroundColor: MaterialStateProperty.resolveWith(
(states) => Color(0xFFE07A72)),
),
);
},
)
What is wrong here? I was trying to use navigation and pass then current cubit to its children but getting errors
Here is the error:
======== Exception caught by widgets library =======================================================
The following ProviderNotFoundException was thrown building Container(bg: MaterialColor(primary value: Color(0xff2196f3)), constraints: BoxConstraints(0.0<=w<=Infinity, h=100.0)):
Error: Could not find the correct Provider<BlocBase<dynamic>> above this BlocBuilder<BlocBase<dynamic>, dynamic> Widget
This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- You added a new provider in your `main.dart` and performed a hot-reload.
To fix, perform a hot-restart.
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a BuildContext that is an ancestor of the provider you are trying to read.
Make sure that BlocBuilder<BlocBase<dynamic>, dynamic> is under your MultiProvider/Provider<BlocBase<dynamic>>.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
```
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>()),
),
}
```
consider using `builder` like so:
```
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context) {
// No longer throws
return Text(context.watch<Example>()),
}
),
}
```
If none of these solutions work, consider asking for help on StackOverflow:
https://stackoverflow.com/questions/tagged/flutter
The relevant error-causing widget was:
Container file:///D:/Development/FlutterDevelopment/xpcover/lib/presentation/vehicle_insurance/vehicle_details_screen.dart:38:13
```
When the exception was thrown, this was the stack:
#0 Provider._inheritedElementOf (package:provider/src/provider.dart:332:7)
#1 Provider.of (package:provider/src/provider.dart:284:30)
#2 ReadContext.read (package:provider/src/provider.dart:610:21)
#3 _BlocBuilderBaseState.initState (package:flutter_bloc/src/bloc_builder.dart:130:36)
#4 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4632:57)
```
==================================================================================================

Navigator.push not working inside async method

I am trying to automatically navigate to another screen after a future method inside an async method. But it's not working on the first-page launch. I keep getting this error.
**Error**
I/flutter ( 5094): Looking up a deactivated widget's ancestor is unsafe.
I/flutter ( 5094): At this point the state of the widget's element tree is no longer stable.
I/flutter ( 5094): 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.
void getPlaceDetails(String placeId, BuildContext ctx, String type) async {
try{
await mainBloc.fetchRideEstimate(context,
mainBloc.pickUpAddress.longitude,
mainBloc.pickUpAddress.latitude,
mainBloc.destinationAddress.longitude,
mainBloc.destinationAddress.latitude).then((value){
Navigator.pop(ctx);
//keeps throwing an error here during first screen launch.
Navigator.push(
context, SlideFromLeftPageRoute(widget:
EstimatedSummaryPage()));
});
}catch(e){
print(e.toString());
}
}
print((thisPlace.placeName));
}
Build Widget
isSearchingFrom?
Expanded(child:
MediaQuery.removePadding(
context: context,
removeTop: true,
child: ListView.separated(
itemBuilder: (context, index){
return FlatButton(
padding: EdgeInsets.zero,
onPressed: () {
getPlaceDetails(pickUpPredictionlist[index].placeId,
_scaffoldKey.currentContext, "from");
},
child: PlacesListTile(
prediction: pickUpPredictionlist[index],
),
);
},
separatorBuilder: (context, index) {
return Divider(
height: 2,
);
},
itemCount: pickUpPredictionlist.length),
)):
Container(),

StreamBuilder<QuerySnapshot>(dirty, state: _StreamBuilderBaseState<QuerySnapshot, AsyncSnapshot<QuerySnapshot>):

I cant able to retrive the data from Firestore and getting Error as below,
════════ Exception caught by widgets library
═══════════════════════════════════════════════════════ The following
assertion was thrown building StreamBuilder(dirty,
state: _StreamBuilderBaseState<QuerySnapshot,
AsyncSnapshot>#e568b): A build function returned null.
The offending widget is: StreamBuilder Build functions
must never return null.
To return an empty space that causes the building widget to fill
available room, return "Container()". To return an empty space that
takes as little room as possible, return "Container(width: 0.0,
height: 0.0)".
The relevant error-causing widget was: StreamBuilder
file:...dart:140:15 When the exception was thrown, this was the stack:
#0 debugWidgetBuilderValue. (package:flutter/src/widgets/debug.dart:300:7)
#1 _Closure.call (dart:core-patch/function.dart)
#2 debugWidgetBuilderValue (package:flutter/src/widgets/debug.dart:321:4)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4569:7)
#4 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:4737:11) ...
Below is my code.
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection("currency").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData){
print('test pharse');
Text("Loading.....");}
else {
List<DropdownMenuItem> currencyItems = [];
for (int i = 0; i < snapshot.data.documents.length; i++) {
DocumentSnapshot snap = snapshot.data.documents[i];
currencyItems.add(
DropdownMenuItem(
child: Text(
snap.documentID,
style: TextStyle(color: Color(0xff11b719)),
),
value: "${snap.documentID}",
),
);
}
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.mail,
size: 25.0, color: Color(0xff11b719)),
SizedBox(width: 50.0),
DropdownButton(
items: currencyItems,
onChanged: (currencyValue) {
final snackBar = SnackBar(
content: Text(
'Selected Currency value is $currencyValue',
style: TextStyle(color: Color(0xff11b719)),
),
);
Scaffold.of(context).showSnackBar(snackBar);
setState(() {
selectedCurrency = currencyValue;
});
},
value: selectedCurrency,
isExpanded: false,
hint: new Text(
"Choose Currency Type",
style: TextStyle(color: Color(0xff11b719)),
),
),
],
);
}
}),
You need to add a return before the Text widget in the !snapshot.hasData section of the StreamBuilder
if (!snapshot.hasData){
print('test phrase');
return Text("Loading.....");
}