Related
I am working on a app were I get data from the ESP32 and display it on a simple page. Here is my code for my sensorpage:
import 'dart:async';
import 'dart:convert' show utf8;
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
class sensorpage extends StatefulWidget {
const sensorpage({Key? key, required this.device}) : super(key: key);
final BluetoothDevice device;
#override
_sensorpageState createState() => _sensorpageState();
}
class _sensorpageState extends State<sensorpage> {
final String SERVICE_UUID = "edb91e04-3e19-11ec-9bbc-0242ac130002";
final String CHARACTERISTIC_UUID = "edb920c0-3e19-11ec-9bbc-0242ac130002";
late bool isReady;
//String val1 = "";
//int pot1 = 0;
FlutterBlue flutterBlue = FlutterBlue.instance;
//late StreamSubscription<ScanResult> scanSubScription;
//late BluetoothDevice targetDevice;
late Stream<List<int>> stream;
#override
void initState() {
super.initState();
isReady = false;
connectToDevice();
}
connectToDevice() async {
if (widget.device == null) {
_Pop();
return;
}
Timer(const Duration(seconds: 15), () {
if (!isReady) {
disconnectFromDevice();
_Pop();
}
});
await widget.device.connect();
discoverServices();
}
disconnectFromDevice() {
if (widget.device == null) {
_Pop();
return;
}
widget.device.disconnect();
}
discoverServices() async {
if (widget.device == null) {
_Pop();
return;
}
List<BluetoothService> services = await widget.device.discoverServices();
services.forEach((service) {
if (service.uuid.toString() == SERVICE_UUID) {
service.characteristics.forEach((characteristic) {
if (characteristic.uuid.toString() == CHARACTERISTIC_UUID) {
characteristic.setNotifyValue(!characteristic.isNotifying);
stream = characteristic.value;
setState(() {
isReady = true;
});
}
});
}
});
if (!isReady) {
_Pop();
}
}
Future<bool> _onWillPop() async {
bool shouldPop = false;
await showDialog(
context: context,
builder: (context) =>
AlertDialog(
title: const Text('Are you sure?'),
content: const Text('Do you want to disconnect device and go back?'),
actions: <Widget>[
ElevatedButton(
onPressed: () {
// shouldPop is already false
},
child: const Text('No')),
ElevatedButton(
onPressed: () async {
await disconnectFromDevice();
Navigator.of(context).pop();
shouldPop = true;
},
child: const Text('Yes')),
],
));
return shouldPop;
}
_Pop() {
Navigator.of(context).pop(true);
}
String _dataParser( List<int> dataFromDevice) {
return utf8.decode(dataFromDevice);
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
appBar: AppBar(
title: const Text('Test'),
),
body: Container(
child: !isReady
? const Center(
child: Text(
"Waiting...",
style: TextStyle(fontSize: 24, color: Colors.red),
),
)
: Container(
child: StreamBuilder<List<int>>(
stream: stream,
builder: (BuildContext context,
AsyncSnapshot<List<int>> snapshot) {
if (snapshot.hasError){
return Text('Error: ${snapshot.error}');
}
if (snapshot.connectionState == ConnectionState.active) {
var currentValue = _dataParser (snapshot.data!);
//val1 = currentValue.split(',')[0];
//pot1 = int.parse(val1);
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Current value',
style: TextStyle(fontSize: 14)),
Text('${currentValue} jawool',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24))
])
);
} else {
return const Text('Check the stream');
}
},
)),
)
));
}
}´
My Problem is that my second container where I display my data is shown as unnecessary but I don´t know why.
I assume you mean this piece of code?
body: Container(
child: !isReady
? const Center(
child: Text(
"Waiting...",
style: TextStyle(fontSize: 24, color: Colors.red),
),
)
: Container(
child: StreamBuilder<List<int>>(
If isReady is true you return Container(child: Container(child: SteamBuilder));
You should change it to this and it should be fine:
body: Container(
child: !isReady
? const Center(
child: Text(
"Waiting...",
style: TextStyle(fontSize: 24, color: Colors.red),
),
)
: StreamBuilder<List<int>>(
The first Container is just wrapping the content based on the boolean and you don't need it. According to the flutter team:
Wrapping a widget in Container with no other parameters set has no effect and makes code needlessly more complex
So instead you can directly do:
body: !isReady ? buildSomething() : Container(....more code);
I'm passing data from a stateful widget, and I access it like in this example (widget.variable)
https://stackoverflow.com/a/50818870/806009
However, occasionally it throws an error (about 1 in 20) for a line in this comparison
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The method '>' was called on null.
Receiver: null
Tried calling: >(10)
if (widget.x > 10){...}
It seems that widget.x is not known at this time. Should I use initState to ensure that value is "ready"?
Such as
#override
void initState() {
super.initState();
this.x = widget.x
}
Full Code
class PlayerSelection extends StatefulWidget {
final int teamId;
final int leagueId;
final League.League league;
final String className;
List<PlayerObj> playersListOfClass;
final int index;
final int remaining;
PlayerSelection({Key key, #required this.teamId, this.leagueId, this.league, this.className, this.playersListOfClass, this.index, this.remaining}) : super(key: key);
#override
_PlayerSelection createState() => _PlayerSelection();
}
class _PlayerSelection extends State<PlayerSelection> {
var playerWidgets = <Widget>[];
List<PlayerObj> selectedPlayers = [];
List<PlayerObj> playerList = [];
PlayerObj draftedPlayer;
List<PlayerClass> playerClassList = [];
bool _isFavorited = false;
Modal modal = new Modal();
int lineupWeek = 0;
int lineupSize = 0;
String intervalLabel = '';
bool _is_full = false;
bool _is_locked = false;
int remaining = 0;
var currentColor = Colors.black;
var rejectedPlayerId;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
resizeToAvoidBottomPadding: false,
appBar: globals.MyAppBar(
leading: IconButton(icon: Icon(Icons.close),
onPressed: (){
Navigator.pop(context, {'player': null,'index': this.widget.index});
}), // go to league route,),
title: Text(widget.className)
),
body: Container(
child: Column(children: [Expanded(child: ListView(children: _getLineup(this.widget.playersListOfClass))),]),
),
);
}
List<Widget>_getLineup(playerList) {
List<Widget> widgets = [];
var index = 0;
playerList.forEach((player) =>
widgets.add(
Padding(
padding: const EdgeInsets.symmetric(horizontal:16.0),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
border: Border(
bottom: BorderSide(width: 1.0, color: Colors.grey.withOpacity(0.3)),
),
),
child: new ListTile(
leading: GestureDetector(
onTap: (){
if(!_is_full) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) =>
Players.PlayerDetail(playerId: player.playerId,
leagueId: widget.leagueId,
playerName: player.playerName,
playerBio: player.playerBio)),
);
}
},
child: ClipOval(
child: _playerPicWidget(player)
),
),
title: GestureDetector(
onTap: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) =>
Players.PlayerDetail(playerId: player.playerId,
leagueId: widget.leagueId,
playerName: player.playerName,
playerBio: player.playerBio)),
);
},
child: _playerNameWidget(player)
),
trailing: _trailingWidget(player),
onTap: () => player.playerPrice <= widget.remaining ? Navigator.pop(context, {'player': player,'index': this.widget.index}) : null,
// return player back
),
),
)
)
);
return widgets;
}
Widget _playerNameWidget(player){
if(this._is_full && !this.selectedPlayers.contains(player)){ //show disabled selection
return Opacity(opacity:.25, child:Text("${player.playerName}"));
}
else {
if(this.widget.league.hasBudget){ // can afford player, not full
if(player.playerPrice <= widget.remaining || this.selectedPlayers.contains(player)){
return Text("${player.playerName}");
}
else { // can't afford player, not full
return Opacity(opacity:.25, child:Text("${player.playerName}"));
}
}
else { // slot still open
return Text("${player.playerName}");
}
}
}
Widget _playerPicWidget(player){
if(player == this.draftedPlayer){
return Opacity(opacity: .25, child: Image.network('${player.playerImageUrl}',
fit: BoxFit.scaleDown,
height: 45,
width: 45,
));
}
else {
if(player.playerPrice <= widget.remaining || this.selectedPlayers.contains(player)){
return Image.network('${player.playerImageUrl}',
fit: BoxFit.scaleDown,
height: 45,
width: 45,
);
}
}
}
Widget _trailingWidget(player){
List<Widget> tWidgets;
double playerOpacity = 1;
if(player.playerPrice > widget.remaining){
playerOpacity = .25;
}
tWidgets = [
Padding(padding: const EdgeInsets.symmetric(horizontal:10.0),
child: Opacity(opacity:playerOpacity, child:Text("\$${globals.commaFormat.format(player.playerPrice)}")),
), Opacity(opacity: playerOpacity, child: Icon(Icons.add))];
return Row(mainAxisSize: MainAxisSize.min, children: tWidgets);
}
}
The issue was a data issue unrelated to the passing of data.
I have a parent widget called createRoutineScreen and it has 7 similar children widget called RoutineFormCard. RoutineFormCard is a form and which has a state _isPostSuccesful of boolean type to tell whether the form is saved to database or not. Now, I have to move to the other screen from createRoutine only when all of it's 7 children has _isPostSuccesful true. How can I access all of children's state from createRoutineScreen widget?
My Code is:
class CreateRoutineScreen extends StatefulWidget {
final String userID;
CreateRoutineScreen({this.userID});
//TITLE TEXT
final Text titleSection = Text(
'Create a Routine',
style: TextStyle(
color: Colors.white,
fontSize: 25,
)
);
final List<Map> weekDays = [
{"name":"Sunday", "value":1},
{"name":"Monday", "value":2},
{"name":"Tuesday", "value":3},
{"name":"Wednesday", "value":4},
{"name":"Thursday", "value":5},
{"name":"Friday", "value":6},
{"name":"Saturday", "value":7},
];
#override
_CreateRoutineScreenState createState() => _CreateRoutineScreenState();
}
class _CreateRoutineScreenState extends State<CreateRoutineScreen> {
Routine routine;
Future<List<dynamic>> _exercises;
dynamic selectedDay;
int _noOfRoutineSaved;
List _keys = [];
Future<List<dynamic>>_loadExercisesData()async{
String url = BASE_URL+ "exercises";
var res = await http.get(url);
var exercisesList = Exercises.listFromJSON(res.body);
//var value = await Future.delayed(Duration(seconds: 5));
return exercisesList;
}
#override
void initState(){
super.initState();
_exercises = _loadExercisesData();
_noOfRoutineSaved = 0;
for (int i = 0; i< 7; i++){
_keys.add(UniqueKey());
}
}
void _changeNoOfRoutineSaved(int a){
setState(() {
_noOfRoutineSaved= _noOfRoutineSaved + a;
});
}
#override
Widget build(BuildContext context) {
print(_noOfRoutineSaved);
return Scaffold(
appBar: AppBar(
title:Text("Create a Routine"),
centerTitle: true,
actions: <Widget>[
FlatButton(
child: Text("Done"),
onPressed: (){
},
),
],
),
body: Container(
color: Theme.of(context).primaryColor,
padding: EdgeInsets.only(top:5.0,left: 10,right: 10,bottom: 10),
child: FutureBuilder(
future: _exercises,
builder: (context, snapshot){
if(snapshot.hasData){
return ListView.builder(
itemCount: widget.weekDays.length,
itemBuilder: (context,index){
return RoutineFormCard(
weekDay: widget.weekDays[index]["name"],
exerciseList: snapshot.data,
userID : widget.userID,
changeNoOfRoutineSaved:_changeNoOfRoutineSaved,
key:_keys[index]
);
},
);
}
else if(snapshot.hasError){
return SnackBar(
content: Text(snapshot.error),
);
}
else{
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.grey,
)
);
}
},
)
),
);
}
}
And my child widget is:
class RoutineFormCard extends StatefulWidget {
final Function createRoutineState;
final String weekDay;
final List<dynamic> exerciseList;
final String userID;
final Function changeNoOfRoutineSaved;
RoutineFormCard({this.createRoutineState,
this.weekDay, this.exerciseList, this.changeNoOfRoutineSaved,
this.userID, Key key}):super(key:key);
#override
_RoutineFormCardState createState() => _RoutineFormCardState();
}
class _RoutineFormCardState extends State<RoutineFormCard> {
bool _checkBoxValue= false;
List<int> _selectedExercises;
bool _inAsyncCall;
bool _successfulPost;
#override
void initState(){
super.initState();
_selectedExercises = [];
_inAsyncCall = false;
_successfulPost= false;
}
void onSaveClick()async{
setState(() {
_inAsyncCall = true;
});
String url = BASE_URL + "users/routine";
List selectedExercises = _selectedExercises.map((item){
return widget.exerciseList[item].value;
}).toList();
String dataToSubmit = jsonEncode({
"weekDay":widget.weekDay,
"userID": widget.userID==null?"5e9eb190b355c742c887b88d":widget.userID,
"exercises": selectedExercises
});
try{
var res =await http.post(url, body: dataToSubmit,
headers: {"Content-Type":"application/json"});
if(res.statusCode==200){
print("Succesful ${res.body}");
widget.changeNoOfRoutineSaved(1);
setState(() {
_inAsyncCall = false;
_successfulPost = true;
});
}
else{
print("Not succesful ${res.body}");
setState(() {
_inAsyncCall = false;
});
}
}catch(err){
setState(() {
_inAsyncCall = false;
});
print(err);
}
}
Widget saveAndEditButton(){
if(_inAsyncCall){
return CircularProgressIndicator();
}
else if(_successfulPost)
{
return IconButton(
icon: Icon(Icons.edit, color: Colors.black,),
onPressed: (){
widget.changeNoOfRoutineSaved(-1);
setState(() {
_successfulPost = false;
});
},
);
}
else{
return FlatButton(child: Text("Save"),
onPressed: !_checkBoxValue&&_selectedExercises.length==0?null:onSaveClick,);
}
}
//Card Header
Widget cardHeader(){
return AppBar(
title: Text(widget.weekDay, style: TextStyle(
fontFamily: "Raleway",
fontSize: 20,
color: Colors.black,),
),
actions: <Widget>[
saveAndEditButton()
],
backgroundColor: Colors.lime[400],
);
}
Widget cardBody(){
return Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
Text("Rest Day"),
Checkbox(
value: _checkBoxValue,
onChanged: (value){
setState(() {
_checkBoxValue = value;
});
},
)
],
),
),
_checkBoxValue?Container():
SearchableDropdown.multiple(
hint: "Select Exercise",
style: TextStyle(color: Colors.black),
items: widget.exerciseList.map<DropdownMenuItem>((item){
return DropdownMenuItem(
child: Text(item.name), value: item
);
}).toList(),
selectedItems: _selectedExercises,
onChanged: (values){
setState(() {
_selectedExercises = values;
});
},
isExpanded: true,
dialogBox: true,
),
],
);
}
#override
Widget build(BuildContext context) {
print("<><><><><><><><><><><>${widget.weekDay} called");
return Card(
elevation: 8.0,
child: Form(
key: GlobalKey(),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
cardHeader(),
_successfulPost?Container():cardBody()
],
),
),
);
}
}
As you can see, I've tried callBack from parent widget which increases or decrease no of form saved from each of the child widget. It does the work but, when one form is saved, parent state is modified and all other children got rebuild which is unnecessary in my opionion. What's the best way to do it?
Try to use GlobalKey instead of UniqueKey for each RoutineFormCard. It will help you to access the state of each RoutineFormCard. You can do it like this :
// 1. In the top of your CreateRoutineScreen file, add this line (make your RoutineFormCardState class public before)
final List<GlobalKey<RoutineFormCardState>> routineFormCardKeys = <GlobalKey<RoutineFormCardState>>[
GlobalKey<RoutineFormCardState>(),
GlobalKey<RoutineFormCardState>(),
GlobalKey<RoutineFormCardState>(),
GlobalKey<RoutineFormCardState>(),
GlobalKey<RoutineFormCardState>(),
GlobalKey<RoutineFormCardState>(),
GlobalKey<RoutineFormCardState>(),
];
// 2. Then construct your RoutineFormCard using the right key
RoutineFormCard(
weekDay: widget.weekDays[index]["name"],
exerciseList: snapshot.data,
userID : widget.userID,
changeNoOfRoutineSaved:_changeNoOfRoutineSaved,
key: routineFormCardKeys[index]
);
// 3. Now you can create a method in CreateRoutineScreen which will check the state of all RoutineFormCard
bool _allRoutineFormCardsCompleted() {
bool result = true;
for (int i = 0; i < 7; i++)
result = result && routineFormCardKeys[i].currentState.isPostSuccessful;
return result;
}
// 4. Finally use the result of the previous method where you want to move on another page
I'm sharing a quick idea to solve your problem, I've not tested it, but I'm ready to improve the answer if needed
Hope this will help!
Here I've one service page where I've displayed all the services, and there I can select multiple services. but I want to select only one service. I've used the checkbox Listitle for the service selection. I want that user can select only one service not multiple services at a time.
Here is code i've tried :
class _AddWalkinServiceScreenState extends State<AddWalkinServiceScreen>
with TickerProviderStateMixin {
List<int> servicesIds = [];
int selected = 0;
Map<String, bool> _selection = {};
List<BspServices.Service> selectedServices = [];
SearchBarController _controller = new SearchBarController();
String _searchText = '';
List<dynamic> finalList = new List();
List<dynamic> searchList = new List();
bool isLoading = false;
AnimationController controller;
Animation<double> animation;
#override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(milliseconds: 500), vsync: this);
animation = CurvedAnimation(parent: controller, curve: Curves.easeInQuint);
controller.forward();
}
Widget _renderServices(AddWalkinServiceViewModel awsVm) {
List lovCountryServices = searchList.length != 0 ? searchList : finalList;
if (lovCountryServices == null || lovCountryServices.length == 0) {
return Container(
child: Center(
child: Text("No Services available for this combination"),
),
);
}
// print(lovCountryServices);
return Container(
child: finalList.length < 1
? ListTile(
leading: CircularProgressIndicator(),
)
: ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8.0),
itemCount: lovCountryServices.length,
itemBuilder: (BuildContext context, int index) {
var item = lovCountryServices[
index]; // should be outside build function
List items = item['services'];
return ExpansionTile(
title: Text(item['name']),
children: List.generate(items.length, (i) {
_selection[items[i]['name']] =
_selection[items[i]['name']] ?? items[i]['isSelected'];
return CheckboxListTile(
title: Text(items[i]['name']),
value: _selection[items[i]['name']] == null
? false
: _selection[items[i]['name']],
onChanged: (val) {
setState(() {
_selection[items[i]['name']] = val;
if (val) {
servicesIds.add(items[i]['id']);
List<BspServices.Service> services =
selectedServices.where((service) {
return service.mainCategory == item['name'];
}).toList();
SubCategory subService = new SubCategory(
id: items[i]['id'],
name: items[i]['name'],
);
List<SubCategory> subCategories = [];
if (services.length < 1) {
subCategories.add(subService);
selectedServices.add(
new BspServices.Service(
mainCategory: item['name'],
mainCategoryId: item['id'],
subCategory: subCategories,
),
);
} else {
print('services in else');
print(services[0].subCategory);
subCategories = services[0].subCategory;
subCategories.add(subService);
}
} else {
servicesIds.removeWhere((service) {
return service == items[i]['id'];
});
List<BspServices.Service> services =
selectedServices.where((service) {
return service.mainCategory == item['name'];
}).toList();
services[0].subCategory.removeWhere((subService) {
return subService.id == items[i]['id'];
});
}
});
print('servicesIds after set state');
print(servicesIds);
},
);
}),
);
},
),
);
}
Widget content(BuildContext context, AddWalkinServiceViewModel awsVm) {
Orientation orientation = MediaQuery.of(context).orientation;
var colorStyles = Theming.colorstyle(context);
final appBar = SearchBar(
controller: _controller,
onQueryChanged: (String query) {
print('Search Query $query');
setState(() {
_searchText = query;
});
_searchFilter();
},
defaultBar: AppBar(
elevation: 0,
centerTitle: true,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
Navigator.pop(context);
// NavigationHelper.navigatetoBack(context);
}),
title: Text('Select Services'),
),
);
return new Scaffold(
backgroundColor: colorStyles['primary'],
appBar: appBar,
bottomNavigationBar: bottomNavigationBar,
body: FadeTransition(
opacity: animation,
child: new Container(
margin: EdgeInsets.only(top: 10),
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(50.0),
topRight: const Radius.circular(50.0),
),
),
child: isLoading ? FadeInUi() : _renderServices(awsVm),
),
),
);
}
#override
Widget build(BuildContext context) {
return new StoreConnector<AppState, AddWalkinServiceViewModel>(
converter: (Store<AppState> store) =>
AddWalkinServiceViewModel.fromStore(store),
onInit: (Store<AppState> store) {
print('store.state.servicesState.servicesByCountry');
print(store
.state.servicesState.servicesByCountry.servicesByCountry[0].name);
Map<String, dynamic> services =
store.state.servicesState.servicesByCountry.toJson();
finalList = services['servicesByCountry'];
print('finalList = $finalList');
},
builder: (BuildContext context, AddWalkinServiceViewModel awsVm) =>
content(context, awsVm),
);
}
}
UPDATED
List<Map> services = [];
List<int> selections = [];
#override
void initState() {
super.initState();
getList();
}
void getList() async {
//get data from internet/api
//for ex. I m using offline data
setState(() {
services = List.generate(
10,
(ind) => {
'name': 'Service Category $ind',
'services': ['Service 1', 'Service 2']
}).toList();
selections = List.generate(10, (ind) => -1).toList();
});
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20),
color: Colors.white,
child: services.length < 1
? ListTile(
leading: CircularProgressIndicator(), title: Text('Loading...'))
: ListView.builder(
itemCount: services.length,
itemBuilder: (con, ind) {
return ExpansionTile(
title: Text('${services[ind]['name']}',
style: TextStyle(color: Colors.black)),
children:
List.generate(services[ind]['services'].length, (ii) {
return CheckboxListTile(
title: Text('${services[ind]['services'][ii]}',
style: TextStyle(color: Colors.green[900])),
value: selections[ind] == ii,
onChanged: (b) {
setState(() {
selections[ind] = ii;
});
});
}).toList());
}));
}
Here I have two pages first is called BSP_signup_terms page and the second is Bsp_Service_page. when I am on BSP_signup_terms on that page I have to select some checkbox based on the selected checkbox it will show me some data. but problem is that it will show me the complete data but when I get back to the BSP_signup_terms from Bsp_signup_page and I am changing the checkbox and then again when I am pressing next button it will not change the result it same as the previous result.
Here is the Image of Output Page
In this image I've attached both screen output when I am selecting only one checkbox it will render some value in service page and when I am back to the Terms and Condition page and select one more checkbox then it will not updating service page
Here is the code I've tried.
BSP_Signup_Terms_Page
class BspLicensedSignupTermsPage extends StatefulWidget {
static const String routeName = "/bspLicensedSignupTerms";
final BspSignupCommonModel bspSignupCommonModel;
BspLicensedSignupTermsPage({
Key key,
#required this.bspSignupCommonModel,
}) : super(key: key);
#override
_BspLicensedSignupTermsPageState createState() =>
_BspLicensedSignupTermsPageState();
}
class _BspLicensedSignupTermsPageState
extends State<BspLicensedSignupTermsPage> {
#override
void initState() {
super.initState();
}
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
bool _isWalkIn = false;
bool _isHome = false;
bool _isOnDemand = false;
Widget _buildselectcheckbox() {
return Text(
AppConstantsValue.appConst['bsplicensedsignupterms']['selectcheck']
['translation'],
);
}
// Walkin
_onCustomerWalkin(value) {
setState(() {
_isWalkIn = value;
});
}
Widget _buildCustomerWalkIn() {
return TudoConditionWidget(
text: AppConstantsValue.appConst['bsplicensedsignupterms']
['CustomerWalkIn']['translation'],
onChanged: (value) {
print(value);
_onCustomerWalkin(value);
},
validate: false,
);
}
// Home
_onCustomerInHome(value) {
setState(() {
_isHome = value;
});
}
Widget _buildCustomerInHome() {
return TudoConditionWidget(
text: AppConstantsValue.appConst['bsplicensedsignupterms']
['CustomerInHome']['translation'],
onChanged: (value) {
_onCustomerInHome(value);
},
validate: false,
);
}
Widget _buildCustomerInHomeHelp() {
return Text(
AppConstantsValue.appConst['bsplicensedsignupterms']['businesscheckhelp']
['translation'],
);
}
// On Demand
_onCustomerOnDemand(value) {
setState(() {
_isOnDemand = value;
});
}
Widget _buildBusinessOnDemand() {
return TudoConditionWidget(
text: AppConstantsValue.appConst['bsplicensedsignupterms']
['BusinessOnDemand']['translation'],
onChanged: (value) {
_onCustomerOnDemand(value);
},
validate: false,
);
}
Widget _buildBusinessOnDemandHelp() {
return Text(AppConstantsValue.appConst['bsplicensedsignupterms']
['businessprovidehelp']['translation']);
}
#override
Widget build(BuildContext context) {
final appBar = AppBar(
title: Text("Bsp Licensed Signup Terms and Condition"),
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
NavigationHelper.navigatetoBack(context);
},
),
centerTitle: true,
);
final bottomNavigationBar = Container(
height: 56,
//margin: EdgeInsets.symmetric(vertical: 24, horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new FlatButton.icon(
icon: Icon(Icons.close),
label: Text('Clear'),
color: Colors.redAccent,
textColor: Colors.black,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
onPressed: () {
_formKey.currentState.reset();
},
),
new FlatButton.icon(
icon: Icon(FontAwesomeIcons.arrowCircleRight),
label: Text('Next'),
color: colorStyles["primary"],
textColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
onPressed: () {
if (_formKey.currentState.validate()) {
if (_isHome == false &&
_isOnDemand == false &&
_isWalkIn == false) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) => ShowErrorDialog(
title: Text('Select Service'),
content: Text(
'Please select atleast one service type to proceed next',
),
));
} else {
BspSignupCommonModel model = widget.bspSignupCommonModel;
model.isWalkin = _isWalkIn;
model.isHome = _isHome;
model.isOnDemand = _isOnDemand;
print(model.toJson());
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
BspServicePage(bspSignupCommonModel: model),
),
);
}
}
},
),
],
),
);
return new Scaffold(
appBar: appBar,
bottomNavigationBar: bottomNavigationBar,
body: Container(
height: double.infinity,
width: double.infinity,
child: Stack(
children: <Widget>[
SingleChildScrollView(
child: SafeArea(
child: Form(
autovalidate: true,
key: _formKey,
child: Scrollbar(
child: SingleChildScrollView(
dragStartBehavior: DragStartBehavior.down,
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: new Container(
decoration: BoxDecoration(
borderRadius: new BorderRadius.circular(25)),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildselectcheckbox(),
_buildCustomerWalkIn(),
_buildCustomerInHome(),
_buildCustomerInHomeHelp(),
_buildBusinessOnDemand(),
_buildBusinessOnDemandHelp(),
],
),
),
),
),
),
),
),
],
),
),
);
}
}
BSP_Service_Page
class BspServicePage extends StatefulWidget {
static const String routeName = "/bspService";
final BspSignupCommonModel bspSignupCommonModel;
BspServicePage({
Key key,
#required this.bspSignupCommonModel,
}) : super(key: key);
#override
_BspServicePageState createState() => _BspServicePageState();
}
class _BspServicePageState extends State<BspServicePage> {
List<int> servicesIds = [];
Map<String, bool> selection = {};
List<BspServices.Service> selectedServices = [];
SearchBarController _controller = new SearchBarController();
String _searchText = '';
bool refreshservices = true;
#override
void initState() {
super.initState();
}
void _showErrorDialog(String message) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) => ShowErrorDialog(
title: Text('An Error Occurred!'),
content: Text(message),
),
);
}
void refresh() {
setState(() {
refreshservices = !refreshservices;
});
}
#override
Widget build(BuildContext context) {
var _bspServiceBloc = new BspServiceBloc();
final appBar = SearchBar(
controller: _controller,
onQueryChanged: (String query) {
print('Search Query $query');
setState(() {
_searchText = query;
});
},
defaultBar: AppBar(
centerTitle: true,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
refresh();
NavigationHelper.navigatetoBack(context);
}),
title: Text('Select Services'),
),
);
final bottomNavigationBar = Container(
height: 56,
// margin: EdgeInsets.symmetric(vertical: 24, horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new FlatButton.icon(
icon: Icon(Icons.close),
label: Text('Clear'),
color: Colors.redAccent,
textColor: Colors.black,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
onPressed: () {
print('reseting the state');
setState(() {
selection = {};
servicesIds = [];
});
},
),
new FlatButton.icon(
icon: Icon(FontAwesomeIcons.arrowCircleRight),
label: Text('Next'),
color: colorStyles["primary"],
textColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
onPressed: () {
BspSignupCommonModel model = widget.bspSignupCommonModel;
model.servicesIds = servicesIds;
model.services = selectedServices;
print('servicesIds at the next button');
print(servicesIds);
print(model.toJson());
if (servicesIds.length == 0) {
_showErrorDialog(
'You need to select at least one service to proceed next!');
} else {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BusinessProfilePage(
bspSignupCommonModel: model,
),
),
);
}
},
),
],
),
);
return new Scaffold(
appBar: appBar,
bottomNavigationBar: bottomNavigationBar,
body: new BspServiceScreen(
bspServiceBloc: _bspServiceBloc,
bspSignupCommonModel: widget.bspSignupCommonModel,
servicesIds: servicesIds,
selection: selection,
searchQuery: _searchText,
selectedServices: selectedServices,
refresh: refresh,
),
);
}
}
Bsp_service_screen
class BspServiceScreen extends StatefulWidget {
final BspServiceBloc _bspServiceBloc;
final String searchQuery;
final List<int> servicesIds;
final Map<String, bool> selection;
final BspSignupCommonModel bspSignupCommonModel;
final List<BspServices.Service> selectedServices;
final Function refresh;
const BspServiceScreen({
Key key,
#required BspServiceBloc bspServiceBloc,
#required this.bspSignupCommonModel,
#required this.servicesIds,
#required this.selection,
#required this.selectedServices,
#required this.refresh,
this.searchQuery,
}) : _bspServiceBloc = bspServiceBloc,
super(key: key);
#override
BspServiceScreenState createState() {
return new BspServiceScreenState(_bspServiceBloc);
}
}
class BspServiceScreenState extends State<BspServiceScreen> {
final BspServiceBloc _bspServiceBloc;
BspServiceScreenState(this._bspServiceBloc);
// Map<String, bool> _selection = {};
#override
void initState() {
super.initState();
bool isHome = widget.bspSignupCommonModel.isHome;
bool isWalkIn = widget.bspSignupCommonModel.isWalkin;
bool isOnDemand = widget.bspSignupCommonModel.isOnDemand;
this._bspServiceBloc.dispatch(LoadBspServiceEvent(
countryId: 1,
isHome: isHome,
isOnDemand: isOnDemand,
isWalkin: isWalkIn,
));
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return BlocBuilder<BspServiceBloc, BspServiceState>(
bloc: widget._bspServiceBloc,
builder: (
BuildContext context,
BspServiceState currentState,
) {
if (currentState is UnBspServiceState) {
return Center(child: CircularProgressIndicator());
}
if (currentState is ErrorBspServiceState) {
return new Container(
child: new Center(
child: new Text(currentState.errorMessage ?? 'Error'),
),
);
}
if (currentState is InBspServiceState) {
// print(
// 'in bsp service state, ${currentState.bspServices.servicesByCountry.length}');
if (currentState.bspServices.servicesByCountry.length == 0) {
return Container(
child: Center(
child: Text("No Services available for this combination"),
),
);
} else {
return new Container(
child:
_renderServices(currentState.bspServices.servicesByCountry),
);
}
}
return Container();
},
);
}
List<ServicesByCountry> finalList = new List();
ListView _renderServices(List<ServicesByCountry> lovCountryServices) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (widget.searchQuery != '') {
finalList.clear();
lovCountryServices.forEach((ServicesByCountry data) {
if (data.name
.toLowerCase()
.contains(widget.searchQuery.toLowerCase())) {
setState(() {
finalList.add(data);
});
} else {
data.services.forEach((ServiceList.Service services) {
if (services.name
.toLowerCase()
.contains(widget.searchQuery.toLowerCase())) {
setState(() {
finalList.add(data);
});
}
});
}
});
} else {
setState(() {
finalList.clear();
finalList.addAll(lovCountryServices);
});
}
});
return ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8.0),
itemCount: finalList.length,
itemBuilder: (BuildContext context, int index) {
ServicesByCountry item = finalList[index];
List itemsList = item.services;
return ExpansionTile(
title: Text(item.name),
children: List.generate(itemsList.length, (i) {
widget.selection[itemsList[i].name] =
widget.selection[itemsList[i].name] ?? itemsList[i].isSelected;
return CheckboxListTile(
title: Text(itemsList[i].name),
value: widget.selection[itemsList[i].name],
onChanged: (val) {
setState(() {
widget.selection[itemsList[i].name] = val;
if (val) {
widget.servicesIds.add(itemsList[i].id);
List<BspServices.Service> services =
widget.selectedServices.where((service) {
return service.mainCategory == item.name;
}).toList();
SubCategory subService = new SubCategory(
id: itemsList[i].id,
name: itemsList[i].name,
);
List<SubCategory> subCategories = [];
if (services.length == 0) {
subCategories.add(subService);
widget.selectedServices.add(
new BspServices.Service(
mainCategory: item.name,
mainCategoryId: item.id,
subCategory: subCategories,
),
);
} else {
print('services in else');
print(services[0].subCategory);
subCategories = services[0].subCategory;
subCategories.add(subService);
}
} else {
widget.servicesIds.removeWhere((service) {
return service == itemsList[i].id;
});
List<BspServices.Service> services =
widget.selectedServices.where((service) {
return service.mainCategory == item.name;
}).toList();
services[0].subCategory.removeWhere((subService) {
return subService.id == itemsList[i].id;
});
}
});
print('widget.servicesIds after set state');
print(widget.servicesIds);
},
);
}),
);
},
);
}
}
You can use setState() after return to the first page:
Navigator.push(context, MaterialPageRoute(builder: (context) => Page2())).then((value) {
setState(() {
// refresh state
});
});
Please try below code:-
First you add one method async method:-
void redirectToNextScreen() async {
final Route route = MaterialPageRoute(
builder: (context) => BspServicePage(bspSignupCommonModel: model));
final result = await Navigator.push(mContext, route);
try {
if (result != null) {
if (result) {
//Return callback here.
}
}
} catch (e) {
print(e.toString());
}
}
Then Next you can call this method in "BSP_Signup_Terms_Page" on Next button Pressed event.
Second you can add below line in "BspServicePage" screen Next and Cancel Events.
Navigator.pop(mContext, true); //true means refresh back page and false means not refresh.