type '_Type' is not a subtype of type 'String' - flutter

So i am a beginner in flutter and am trying to learn via tutorials, so here I am trying to make todo app using sqflite and everything is perfect and no error is shown in the editor but on clicking floating action button in notelist file it shows this error-
The following _TypeError was thrown building Builder:
type '_Type' is not a subtype of type 'String'
heres my main.dart file
void main() {
runApp(MaterialApp(
home: NoteList(),
));
}
here notelist
class NoteList extends StatefulWidget {
const NoteList({Key? key}) : super(key: key);
#override
_NoteListState createState() => _NoteListState();
}
class _NoteListState extends State<NoteList> {
int count = 0;
DatabaseHelper databaseHelper = DatabaseHelper();
late List<Note> noteList;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Note List'),
),
body: getNoteListView(),
floatingActionButton: FloatingActionButton(
onPressed: () {
debugPrint('fab clicked');
navigateToDetail(Note('', '', 2 ,''),'Add Note');
},
child: Icon(Icons.add),
),
);
}
ListView getNoteListView(){
return ListView.builder(
itemCount: count,
itemBuilder: (context, index){
return Card(
color: Colors.white,
elevation: 2.0,
child: ListTile(
leading: CircleAvatar(
backgroundColor: getPriorityColor(this.noteList[index].priority),
child: getPriorityIcon(this.noteList[index].priority),
),
title: Text(this.noteList[index].title!,),
subtitle: Text(this.noteList[index].date!),
trailing: IconButton(onPressed: (){
_delete(context, noteList[index]);
},
icon: Icon(Icons.delete),
),
onTap: (){
debugPrint('tapped');
navigateToDetail(noteList[index],'Edit Note');
},
),
);
}
);
}
void navigateToDetail(Note note, String title) async{
bool result = await Navigator.push(context, MaterialPageRoute(builder: (context) {
return NoteDetail(appBarTitle: Title, note: note);
}));
if (result == true) {
updateListView();
}
}
// Returns the priority color
Color getPriorityColor(int? priority) {
switch (priority) {
case 1:
return Colors.red;
break;
case 2:
return Colors.yellow;
break;
default:
return Colors.yellow;
}
}
// Returns the priority icon
Icon getPriorityIcon(int? priority) {
switch (priority) {
case 1:
return Icon(Icons.play_arrow);
break;
case 2:
return Icon(Icons.keyboard_arrow_right);
break;
default:
return Icon(Icons.keyboard_arrow_right);
}
}
void _delete(BuildContext context, Note note) async {
int? result = await databaseHelper.deleteNote(note.id);
if (result != 0) {
_showSnackBar(context, 'Note Deleted Successfully');
updateListView();
}
}
void _showSnackBar(BuildContext context, String message) {
final snackBar = SnackBar(content: Text(message));
Scaffold.of(context).showSnackBar(snackBar);
}
void updateListView() {
final Future<Database> dbFuture = databaseHelper.initializeDatabase();
dbFuture.then((database) {
Future<List<Note>> noteListFuture = databaseHelper.getNoteList();
noteListFuture.then((noteList) {
setState(() {
this.noteList = noteList;
this.count = noteList.length;
});
});
});
}
}
and heres notedetail file
class NoteDetail extends StatefulWidget {
final Note note;
final appBarTitle;
NoteDetail( {Key? key,required this.appBarTitle, required this.note}) : super(key: key);
#override
_NoteDetailState createState() => _NoteDetailState(this.note, this.appBarTitle);
}
class _NoteDetailState extends State<NoteDetail> {
static var _priorities = ['High', 'Low'];
DatabaseHelper helper = DatabaseHelper();
TextEditingController titleController = TextEditingController();
TextEditingController descController = TextEditingController();
String appBarTitle;
Note note;
_NoteDetailState(this.note , this.appBarTitle);
#override
Widget build(BuildContext context) {
titleController.text = note.title!;
descController.text = note.description!;
return Scaffold(
appBar: AppBar(
title: Text(appBarTitle),
),
body: Container(
padding: EdgeInsets.all(10),
child: ListView(
children: [
ListTile(
title: DropdownButton(
items: _priorities.map((dropDownStringItem) {
return DropdownMenuItem (
value: dropDownStringItem,
child: Text(dropDownStringItem),
);
}).toList(),
value: getPriorityAsString(note.priority),
onChanged: (valueSelectedByUser) {
setState(() {
debugPrint('User selected $valueSelectedByUser');
updatePriorityAsInt(valueSelectedByUser);
});
}
),
),
SizedBox(height: 10,),
Container(
child: TextField(
controller: titleController,
onChanged: (value) {
debugPrint('something changed in the title textfield ');
updateTitle();
},
decoration: InputDecoration(
labelText: 'Title',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
)
),
),
),
SizedBox(height: 10,),
Container(
child: TextField(
controller: descController,
onChanged: (value) {
debugPrint('something changed in the description textfield ');
updateDescription();
},
decoration: InputDecoration(
labelText: 'Description',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
)
),
),
),
Container(
padding: EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 120,
height: 50,
padding: EdgeInsets.all(5),
child: ElevatedButton(onPressed: (){
debugPrint('add button clicked');
_save();
}, child: Text('Save',
style: TextStyle(
fontSize: 18
),
)
),
),
Container(
width: 120,
height: 50,
padding: EdgeInsets.all(5),
child: ElevatedButton(onPressed: (){
_delete();
debugPrint('Delete button clicked');
}, child: Text('Delete',
style: TextStyle(
fontSize: 18
),)),
),
],
),
)
],
),
),
);
}
// Convert int priority to String priority and display it to user in DropDown
String getPriorityAsString(int? value) {
String priority = '';
switch (value) {
case 1:
priority = _priorities[0]; // 'High'
break;
case 2:
priority = _priorities[1]; // 'Low'
break;
}
return priority;
}
// Convert the String priority in the form of integer before saving it to Database
void updatePriorityAsInt(var value) {
switch (value) {
case 'High':
note.priority = 1;
break;
case 'Low':
note.priority = 2;
break;
}
}
// Update the title of Note object
void updateTitle(){
note.title = titleController.text;
}
// Update the description of Note object
void updateDescription() {
note.description = descController.text;
}
void _delete() async {
moveToLastScreen();
// Case 1: If user is trying to delete the NEW NOTE i.e. he has come to
// the detail page by pressing the FAB of NoteList page.
if (note.id == null) {
_showAlertDialog('Status', 'No Note was deleted');
return;
}
// Case 2: User is trying to delete the old note that already has a valid ID.
int? result = await helper.deleteNote(note.id);
if (result != 0) {
_showAlertDialog('Status', 'Note Deleted Successfully');
} else {
_showAlertDialog('Status', 'Error Occured while Deleting Note');
}
}
void moveToLastScreen() {
Navigator.pop(context, true);
}
void _showAlertDialog(String title, String message) {
AlertDialog alertDialog = AlertDialog(
title: Text(title),
content: Text(message),
);
showDialog(
context: context,
builder: (_) => alertDialog
);
}
// Save data to database
void _save() async {
moveToLastScreen();
note.date = DateFormat.yMMMd().format(DateTime.now());
int? result;
if (note.id != null) { // Case 1: Update operation
result = await helper.updateNote(note);
} else { // Case 2: Insert Operation
result = await helper.insertNote(note);
}
if (result != 0) { // Success
_showAlertDialog('Status', 'Note Saved Successfully');
} else { // Failure
_showAlertDialog('Status', 'Problem Saving Note');
}
}
}

This looks like a spelling mistake.
void navigateToDetail(Note note, String title) async{
...
// change Title into title
return NoteDetail(appBarTitle: title, note: note);
...

Related

Flutter weird bug with widget function, sometimes in runs 2 times instead of one

This function have futureBuilder inside and returns another function that returns ListView.
Also I do not want the user to download information from the Internet every time they visit the page, so I put the information downloaded from the Internet into a static variable in class, so i make if else. If this static variable length != 0 , it return me listView instead of make server request with futureBuilder
function that works 2 times instead of one is ListView body
And also this bug created after i added purchases to my application, maybe something wrong there. I don't have any ideas why this happend
this is my complete code
class AddCheckList extends StatefulWidget {
const AddCheckList({Key? key}) : super(key: key);
#override
_AddCheckListState createState() => _AddCheckListState();
}
class _AddCheckListState extends State<AddCheckList> {
String xQueryReqestForCheckListNames = "element CheckListList {for \$a in PACK/OBJECT where (\$a/#inUse = 'True') order by \$a/#name return element CheckList {attribute name {\$a/#name}, attribute sPaid {\$a/#isPaid},attribute oid {\$a/#oid} }}";
String serverLink = "http...";
int addCheckListMethod = 0;
// if addCheckListMethod == 0, then data will download from server and checkLists will be added from server xml data
// if addCheckListMethod == 1, then data will download from assets, and checkLists will be added from assets xml data with getXmlData function
final InAppPurchase _inAppPurchase = InAppPurchase.instance;
final String _productID = '1d7ea644f690ffa';
bool _available = true;
List<ProductDetails> _products = [];
List<PurchaseDetails> _purchases = [];
StreamSubscription<List<PurchaseDetails>>? _subscription;
#override
void initState() {
final Stream<List<PurchaseDetails>> purchaseUpdated = _inAppPurchase.purchaseStream;
_subscription = purchaseUpdated.listen((purchaseDetailsList) {
setState(() {
_purchases.addAll(purchaseDetailsList);
_listenToPurchaseUpdated(purchaseDetailsList);
});
}, onDone: () {
_subscription!.cancel();
}, onError: (error) {
_subscription!.cancel();
});
_initialize();
super.initState();
}
#override
void dispose() {
_subscription!.cancel();
super.dispose();
}
void _initialize() async {
_available = await _inAppPurchase.isAvailable();
List<ProductDetails> products = await _getProducts(
productIds: Set<String>.from(
[_productID],
),
);
setState(() {
_products = products;
});
}
void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) {
purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async {
switch (purchaseDetails.status) {
case PurchaseStatus.pending:
// _showPendingUI();
break;
case PurchaseStatus.purchased:
case PurchaseStatus.restored:
// bool valid = await _verifyPurchase(purchaseDetails);
// if (!valid) {
// _handleInvalidPurchase(purchaseDetails);
// }
break;
case PurchaseStatus.error:
print(purchaseDetails.error!);
// _handleError(purchaseDetails.error!);
break;
default:
break;
}
if (purchaseDetails.pendingCompletePurchase) {
await _inAppPurchase.completePurchase(purchaseDetails);
}
});
}
Future<List<ProductDetails>> _getProducts({required Set<String> productIds}) async {
ProductDetailsResponse response = await _inAppPurchase.queryProductDetails(productIds);
return response.productDetails;
}
void _subscribe({required ProductDetails product}) {
final PurchaseParam purchaseParam = PurchaseParam(productDetails: product);
_inAppPurchase.buyNonConsumable(
purchaseParam: purchaseParam,
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(padding: EdgeInsets.all(14.0), child: listViweBody(addCheckListMethod, serverLink, xQueryReqestForCheckListNames, _products,_subscribe)),
);
}
}
Widget listView(addCheckListMethod, serverLink, product,_subscribe) {
return ListView.separated(
itemCount: CheckListModel.checkListModelNamesFromServer.length,
itemBuilder: (BuildContext context, int index) {
if (CheckListModel.checkListModelNamesFromServer[index].ispaid == false) {
return InkWell(
onTap: () async {
CheckListModel checkList = CheckListModel('', 0, '', 0, 0, 0, '', [], '');
if (addCheckListMethod == 0) {
String xQueryReqestForCheckList = 'for \$a in PACK/OBJECT where \$a/#name="${CheckListModel.checkListModelNamesFromServer[index].name}" return \$a';
var data = await CheckListModel.getDataFromServer(xQueryReqestForCheckList, serverLink);
CheckListModel.addCheckListFromServer(checkList, data);
} else {
CheckListModel.addCheckList(index, checkList);
}
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Добавить описание"),
content: Container(
decoration: BoxDecoration(border: Border.all(color: Color.fromARGB(Desing.colorFromARGBBtn[0], Desing.colorFromARGBBtn[1], Desing.colorFromARGBBtn[2], Desing.colorFromARGBBtn[3]), width: 1), borderRadius: BorderRadius.circular(10)),
child: TextField(
decoration: new InputDecoration.collapsed(hintText: "Описание", border: InputBorder.none),
maxLines: null,
onChanged: (String value) async {
checkList.description = value;
},
),
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("Отменить"),
),
TextButton(
onPressed: () async {
await checkList.writeToFile(checkList.toJson());
CheckListModel.checkLists.add(checkList);
Navigator.pushNamed(context, '/beforeMainCheckList', arguments: {'index': CheckListModel.checkLists.length - 1});
},
child: Text('Добавить'))
],
);
});
},
child: Container(
decoration: BoxDecoration(border: Border.all(color: Color.fromARGB(Desing.colorFromARGBBtn[0], Desing.colorFromARGBBtn[1], Desing.colorFromARGBBtn[2], Desing.colorFromARGBBtn[3]), width: 1), borderRadius: BorderRadius.circular(10)),
width: 50,
height: 80,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"${CheckListModel.checkListModelNamesFromServer[index].name}",
style: TextStyle(fontSize: 30),
)
],
),
),
);
} else {
if (product.length != 0) {
return showPurchaseCheckLists(product, index,_subscribe);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}
},
separatorBuilder: (BuildContext context, int index) {
return Container(
height: 14,
);
});
}
Widget showPurchaseCheckLists(product, index,_subscribe) {
int getCurrentProduct() {
String? checkListModelid = CheckListModel.checkListModelNamesFromServer[index].checkListId?.toLowerCase();
int indexx = 0;
for (int i = 0; i < product.length; i++) {
if (checkListModelid == product[i].id) {
indexx = i;
}
}
return indexx;
}
return InkWell(
child: Container(
decoration: BoxDecoration(border: Border.all(color: Color.fromARGB(Desing.colorFromARGBBtn[0], Desing.colorFromARGBBtn[1], Desing.colorFromARGBBtn[2], Desing.colorFromARGBBtn[3]), width: 1), borderRadius: BorderRadius.circular(10), color: Color.fromARGB(255, 240, 240, 240)),
width: 50,
height: 80,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"${CheckListModel.checkListModelNamesFromServer[index].name}",
style: TextStyle(fontSize: 25),
),
Text("Купить за ${product[getCurrentProduct()].price}")
],
),
),
onTap: () {
_subscribe(product: product[getCurrentProduct()]);
},
);
}
Widget listViweBody(addCheckListMethod, serverLink, xQueryReqestForCheckListNames, product,_subscribe) {
if (CheckListModel.checkListModelNamesFromServer.length == 0) {
return FutureBuilder(
future: CheckListModel.getDataFromServer(xQueryReqestForCheckListNames, serverLink),
builder: (context, data) {
if (data.connectionState == ConnectionState.done) {
return listView(addCheckListMethod, serverLink, product,_subscribe);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
});
} else {
return listView(addCheckListMethod, serverLink, product,_subscribe);
}
}

Snackbar showing twice due to stacked screen Flutter, How can i avoid it?

I don't want to show Snackbar on stacked screen. When user taps on Signup from LoginScreen. Then, SignUpScreen stacked over LoginScreen. but the problem is both implements same ProviderListener and therefore, It shows Snackbar multiple times. How can I avoid it? , How can I make sure that, If current route is SignUp then show Snackbar
UserAuthService.dart
import 'package:notifications/domain/repository/firebase_repository/firebase_user_repo.dart';
import 'package:notifications/domain/services/auth_service/all_auth_builder.dart';
import 'package:notifications/export.dart';
import 'package:notifications/resources/local/local_storage.dart';
enum AuthenticationStatus {
loading,
error,
success,
}
class UserAuthService extends ChangeNotifier {
final authBuilder = AllTypeAuthBuilder();
String? _errorMsg, _sessionID;
AuthenticationStatus _status = AuthenticationStatus.loading;
EmailLinkAuthenticationRepo? _repo;
String? get errorMsg => this._errorMsg;
void get _setDefault {
_errorMsg = null;
//_sessionID = null;
}
String? get sessionID {
return LocallyStoredData.getSessionID();
}
void logOut() {
return LocallyStoredData.deleteUserKey();
}
Future<bool> userExists(String userID) async {
final isExists = await authBuilder.checkUserExists(userID);
return isExists ? true : false;
}
Future<bool> login() async {
_setDefault;
try {
await authBuilder.login();
return true;
} on BaseException catch (e) {
log("Exception $e");
_errorMsg = e.msg;
notifyListeners();
return false;
}
}
Future<bool> register(String username, String email, String password) async {
_setDefault;
try {
await authBuilder.register(username, email, password);
return true;
} on BaseException catch (e) {
log("Exception $e");
_errorMsg = e.msg;
notifyListeners();
return false;
}
}
Future<bool> signIn(String userID, String password) async {
_setDefault;
try {
await authBuilder.signIn(userID, password);
return true;
} on BaseException catch (e) {
log("Exception ${e.msg}");
_errorMsg = e.msg;
notifyListeners();
}
return false;
}
void loginWithEmail(String email) async {
_setDefault;
try {
_repo = await authBuilder.loginWithEmail(email);
_repo!.onLinkListener(
onSuccess: _onSuccess,
onError: _onError,
);
} on BaseException catch (e) {
log("Exception ${e.msg}");
_errorMsg = e.msg;
}
notifyListeners();
}
Future<bool> _onSuccess(PendingDynamicLinkData? linkData) async {
_setDefault;
try {
log("OnLinkAuthenticate");
await _repo!.onLinkAuthenticate(linkData);
return true;
} on BaseException catch (e) {
log("Error onSucess: $e");
_errorMsg = e.msg;
notifyListeners();
}
return false;
}
Future<dynamic> _onError(OnLinkErrorException? error) async {
log("Error $error in Link");
}
Future<void> tryTo(Function callback) async {
try {
await callback();
} on BaseException catch (e) {
_errorMsg = e.msg;
}
}
}
LoginScreen.dart
class LoginScreen extends StatefulWidget {
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _userIDController = TextEditingController(),
_passwordController = TextEditingController();
final _formKey = GlobalKey<FormState>();
bool? isAuthenticated;
#override
initState() {
super.initState();
}
#override
void dispose() {
_userIDController.dispose();
_passwordController.dispose();
super.dispose();
}
_onGoogleLogin() async {
context.read(loginPod).login();
}
_onLoginButtonTap() {
networkCheckCallback(context, () async {
if (_formKey.currentState!.validate()) {
WidgetUtils.showLoaderIndicator(context, 'Loading...');
final isSignedIn = await context
.read(loginPod)
.signIn(_userIDController.text, _passwordController.text);
Navigator.pop(context);
if (isSignedIn) Beamer.of(context).beamToNamed(Routes.home);
}
});
}
_resetAuthenticateState() {
if (isAuthenticated != null)
setState(() {
isAuthenticated = null;
});
}
onUsernameChange(String? value) async {
final error = await hasNetworkError();
if (_userIDController.text.isNotEmpty && error == null) {
isAuthenticated = await context.read(loginPod).userExists(value!);
setState(() {});
return;
}
_resetAuthenticateState();
}
onPasswordChange(String? value) {
//Code goes here....
}
loginWith(BuildContext context, LoginType type) {
switch (type) {
case LoginType.emailLink:
Beamer.of(context).beamToNamed(Routes.email_link_auth);
break;
case LoginType.idPassword:
Beamer.of(context).beamToNamed(Routes.login_id_pass);
break;
case LoginType.googleAuth:
Beamer.of(context).beamToNamed(Routes.login_with_google);
break;
case LoginType.unknown:
Beamer.of(context).beamToNamed(Routes.register);
break;
}
}
Future<bool> _onBackPress(_) async {
return await showDialog<bool>(
context: _,
builder: (context) {
return AlertDialog(
title: Text("Do you want to exit?"),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text("OK")),
TextButton(
onPressed: () {
Beamer.of(_).popRoute();
},
child: Text("Cancel"))
],
);
}) ??
false;
}
_onLoginStatus(BuildContext _, UserAuthService service) {
if (service.errorMsg != null)
_.showErrorBar(
content: Text(
"WithLogin" + service.errorMsg!,
style: TextStyle(fontSize: 12.sp),
));
}
#override
Widget build(BuildContext _) {
return ProviderListener(
onChange: _onLoginStatus,
provider: loginPod,
child: WillPopScope(
onWillPop: () => _onBackPress(_),
child: Scaffold(
body: SingleChildScrollView(
child: SizedBox(height: 1.sh, child: _buildLoginScreen())),
),
),
);
}
Widget _buildLoginScreen() {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
//_buildVrtSpacer(60),
_buildHeading(),
//_buildVrtSpacer(30),
_buildForm(),
//_buildVrtSpacer(30),
_buildIconButtons(),
_buildSignUpButton(),
],
);
}
BoldHeadingWidget _buildHeading() =>
BoldHeadingWidget(heading: AppStrings.login);
ResponsiveVrtSpacer _buildVrtSpacer(double value) =>
ResponsiveVrtSpacer(space: value);
Widget _buildForm() {
return CustomForm(
formKey: _formKey,
child: Column(
children: [
_buildUsernameField(),
_buildVrtSpacer(10),
_buildPasswordField(),
_buildForgetPassword(),
_buildLoginButton(),
],
),
);
}
Widget _buildPasswordField() {
return CustomTextFieldWithLabeled(
controller: _passwordController,
label: AppStrings.password,
hintText: AppStrings.password,
onValidate: (String? value) =>
(value!.isEmpty) ? AppStrings.emptyPasswordMsg : null,
obscureText: true,
onChange: onPasswordChange,
icon: CupertinoIcons.lock);
}
Widget _buildUsernameField() {
return CustomTextFieldWithLabeled(
controller: _userIDController,
label: AppStrings.usernameOrEmail,
hintText: AppStrings.usernameOrEmail1,
icon: CupertinoIcons.person,
onChange: onUsernameChange,
onValidate: (String? value) =>
(value!.isEmpty) ? AppStrings.emptyUserIDMsg : null,
suffixIcon: isAuthenticated == null
? null
: (isAuthenticated!
? CupertinoIcons.checkmark_alt_circle_fill
: CupertinoIcons.clear_circled_solid),
suffixColor: isAuthenticated == null
? null
: (isAuthenticated! ? Colors.green : Styles.defaultColor));
}
Widget _buildIconButtons() {
return Column(
children: [
Text("Or", style: TextStyle(fontSize: 14.sp)),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(width: 10),
_buildIconButton(
iconPath: 'assets/icons/email-icon.svg', onTap: () {}),
const SizedBox(width: 8),
_buildIconButton(
iconPath: 'assets/icons/icons8-google.svg',
onTap: _onGoogleLogin),
],
),
],
);
}
Widget _buildIconButton(
{required String iconPath, required VoidCallback onTap}) {
return GestureDetector(
onTap: onTap,
child: SvgPicture.asset(
iconPath,
height: 30.sp,
width: 30.sp,
));
}
Widget _buildSignUpButton() {
return Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
AppStrings.dontHaveAccount,
style: TextStyle(color: Colors.black54, fontSize: 14.sp),
),
const SizedBox(height: 5),
CustomTextButton(
title: "Sign Up",
onPressed: () => Beamer.of(context).beamToNamed(Routes.register)),
],
);
}
Widget _buildLoginButton() {
return DefaultElevatedButton(
onPressed: _onLoginButtonTap,
title: AppStrings.login,
);
}
Widget _buildForgetPassword() {
return Align(
alignment: Alignment.centerRight,
child: TextButton(
style: ButtonStyle(
overlayColor: MaterialStateProperty.all(Color(0x11000000)),
foregroundColor: MaterialStateProperty.all(Color(0x55000000)),
),
onPressed: () {},
child: Text("Forget Password?"),
),
);
}
}
SignUpScreen.dart
class SignUp extends StatefulWidget {
const SignUp({Key? key}) : super(key: key);
#override
_SignUpState createState() => _SignUpState();
}
class _SignUpState extends State<SignUp> {
final _formKey = GlobalKey<FormState>();
final _usernameController = TextEditingController(text: "hammad11"),
_emailController = TextEditingController(text: "mason#gmail.com"),
_passwordController = TextEditingController(text: "ha11"),
_confirmPassController = TextEditingController(text: "ha11");
_onValidate(String? value, ValidationType type) {
switch (type) {
case ValidationType.username:
if (value!.isNotEmpty && value.length < 8)
return "Username Must Be 8 characters long";
else if (value.isEmpty) return "Username required";
return null;
case ValidationType.email:
if (value!.isEmpty)
return "Email required";
else if (!value.isEmail) return "Please enter a Valid Email";
return null;
case ValidationType.password:
if (value!.isEmpty)
return "Password required";
else if (value.isAlphabetOnly || value.isNumericOnly)
return "Password must be AlphaNumeric";
return null;
case ValidationType.confirmPassword:
if (value!.isEmpty)
return "Confirm Password required";
else if (value != _passwordController.text)
return "Password doesn't match";
return null;
}
}
_onRegister() async {
//Clears any snackbar opened due to Error or Multiple clicks
//ScaffoldMessenger.of(context).clearSnackBars();
log("SignUp -> _onRegisterTap ");
if (_formKey.currentState!.validate()) {
WidgetUtils.showLoaderIndicator(context, "Please wait! Loading.....");
final isLoggedIn = await context.read(loginPod).register(
_usernameController.text,
_emailController.text,
_passwordController.text,
);
await Beamer.of(context).popRoute();
if (isLoggedIn) Beamer.of(context).beamToNamed(Routes.login);
} else
log("Form Input Invalid");
}
_onChanged(_, UserAuthService service) async {
if (service.errorMsg != null) WidgetUtils.snackBar(_, service.errorMsg!);
// if (!service.isLoading) await Beamer.of(context).popRoute();
// if (service.taskCompleted) {
// log("User Added Successfully");
// Beamer.of(context).popToNamed(
// Routes.login_id_pass,
// replaceCurrent: true,
// );
// } else {
// WidgetUtils.snackBar(context, service.errorMsg!);
// }
}
_alreadyHaveAccount() => Beamer.of(context).popToNamed(Routes.main);
#override
Widget build(BuildContext context) {
return ProviderListener(
provider: loginPod,
onChange: _onChanged,
child: Scaffold(
body: LayoutBuilder(builder: (context, constraints) {
return SingleChildScrollView(
child: SizedBox(
height: 1.sh,
child: Column(
children: [
_buildSpacer(50),
BoldHeadingWidget(heading: "Sign Up"),
CustomForm(
child: Column(
children: [
CustomTextFieldWithLabeled(
label: "Username",
hintText: "Type Username",
onValidate: (value) =>
_onValidate(value, ValidationType.username),
controller: _usernameController,
icon: CupertinoIcons.person),
CustomTextFieldWithLabeled(
label: "Email",
hintText: "Type Email",
controller: _emailController,
onValidate: (value) =>
_onValidate(value, ValidationType.email),
icon: CupertinoIcons.envelope),
CustomTextFieldWithLabeled(
label: "Password",
hintText: "Type Password",
controller: _passwordController,
onValidate: (value) =>
_onValidate(value, ValidationType.password),
icon: CupertinoIcons.lock),
CustomTextFieldWithLabeled(
label: "Confirm Password",
hintText: "Type Confirm Password",
onValidate: (value) => _onValidate(
value, ValidationType.confirmPassword),
controller: _confirmPassController,
icon: CupertinoIcons.lock),
Row(
children: [
Checkbox(
value: true,
onChanged: (value) {},
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
),
Flexible(
child: Text(
"I accept Terms & Conditions and the Privacy Policy",
style: TextStyle(fontSize: 13.sp),
),
),
],
),
_buildSpacer(10),
DefaultElevatedButton(
title: "Sign Up",
onPressed: _onRegister,
),
],
),
formKey: _formKey,
),
const SizedBox(height: 10),
CustomTextButton(
onPressed: () => Beamer.of(context)
.popToNamed(Routes.login, stacked: false),
title: AppStrings.alreadyHaveAccount,
),
// Spacer(flex: 2),
],
),
),
);
}),
),
);
}
ResponsiveVrtSpacer _buildSpacer(double value) =>
ResponsiveVrtSpacer(space: value.h);
}
showsnackbar(String message, context) {
final snackbar = SnackBar(
content: Text(message),
duration: Duration(seconds: 2),
);
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context).showSnackBar(snackbar);
}
Use this function to call snackbar it will remove the current snackbar and will only show the latest.

ImagePicker with Bloc - Flutter

How to add a stream with a picture(ImagePicker) in the bloc architecture, which can be selected from the phone, what should the widget look like?
My example is without validation whether it is to be included?
How should I work in a bloc along with the avatar photo?
I do not know how to approach it and what steps would be taken when it comes to both validation of such a photo, if possible, and the use of stream with bloc, any help is very welcome.
class ProfileView extends StatefulWidget {
const ProfileView({Key? key}) : super(key: key);
#override
_ProfileViewState createState() => _ProfileViewState();
static Route route() {
return MaterialPageRoute<void>(builder: (_) => ProfileView());
}
}
class _ProfileViewState extends State<ProfileView> {
final bloc = Bloc();
#override
Widget build(BuildContext context) {
return Scaffold(
body: _profilePage(context),
);
}
Widget _profilePage(BuildContext context) {
return ColorfulSafeArea(
color: orange,
child: Center(
child: Column(
children: [
_changeAvatarButton(context),
SizedBox(height: 15),
_usernameTile(),
SizedBox(height: 5),
_cityTile(),
SizedBox(height: 60),
],
),
),
);
// });
}
// Profile avatar
Widget _avatar() {
return CircleAvatar(
radius: 83,
backgroundColor: orange,
child: CircleAvatar(
backgroundColor: Colors.white,
radius: 80,
child: Icon(personIcon, size: 60, color: orange),
),
);
}
Widget _changeAvatarButton(BuildContext context) {
return Column(
children: [
CircleAvatar(
radius: 83,
backgroundColor: orange,
child: _image == null
? _avatar()
: ClipRRect(
borderRadius: BorderRadius.circular(83),
child: Image.file(
_image!,
height: 160,
// Change the size up or down accordingly border radius
width: 160,
// Change the size up or down accordingly border radius
fit: BoxFit.cover,
)),
),
CustomButtonText(
onPressed: () {
_showPicker(context);
},
title: changePhoto,
textColor: teal),
],
);
}
File? _image;
final picker = ImagePicker();
Future getImage() async {
final pickedFile = await picker.getImage(
source: ImageSource.gallery, maxWidth: 1800, maxHeight: 1800);
setState(() {
if (pickedFile != null) {
_image = File(pickedFile.path);
} else {
print(getImageText);
}
});
}
Future getCameraImage() async {
final pickedFile = await picker.getImage(
source: ImageSource.camera,
maxWidth: 1800,
maxHeight: 1800,
);
setState(() {
if (pickedFile != null) {
_image = File(pickedFile.path);
} else {
print(getImageText);
}
});
}
void _showPicker(BuildContext context) {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext bc) {
return SafeArea(
child: Container(
child: Wrap(
children: <Widget>[
ListTile(
leading: Icon(photoIcon),
title: Text(showPickerPhoto),
onTap: () async {
await getImage();
Navigator.of(context).pop();
}),
ListTile(
leading: Icon(cameraIcon),
title: Text(showPickerCamera),
onTap: () async {
await getCameraImage();
Navigator.of(context).pop();
},
),
],
),
),
);
});
}
Widget _usernameTile() {
return StreamBuilder(
stream: bloc.name,
builder: (context, snapshot) {
return CustomTextField(
title: 'Name',
obscureText: false,
colour: Colors.black,
keyboardType: TextInputType.name,
labelText: labelTextUsername,
onChanged: bloc.changeName,
errorText: snapshot.error != null ? 'invalid name' : null
// onChanged: (newValue) {
// bloc.changeName(newValue);
// },
);
});
}
Widget _cityTile() {
return StreamBuilder(
stream: bloc.city,
builder: (context, snapshot) {
return CustomTextField(
title: 'City',
obscureText: false,
colour: Colors.black,
keyboardType: TextInputType.name,
labelText: labelTextCity,
onChanged: bloc.changeCity,
errorText: snapshot.error != null ? 'invalid city' : null
);
});}
}
Validators looks in this way:
class Validators {
static final RegExp _nameRegExp = RegExp(
r'^(?=.*[a-z])[A-Za-z ]{3,}$',
);
final validateName =
StreamTransformer<String, String>.fromHandlers(handleData: (name, sink) {
if (name.contains(_nameRegExp)) {
sink.add(name);
} else {
sink.addError('Enter a valid name');
}
});
static final RegExp _cityRegExp = RegExp(
r'^[a-zA-Z]+(?:[\s-][a-zA-Z]+)*$',
);
final validateCity =
StreamTransformer<String, String>.fromHandlers(handleData: (city, sink) {
if (city.contains(_cityRegExp)) {
sink.add(city);
} else {
sink.addError('Enter a valid city');
}
});
static final RegExp _avatarRegExp = RegExp(
r'/.*\.(gif|jpe?g|bmp|png)$/igm',
);
final validateAvatar =
StreamTransformer<String, String>.fromHandlers(handleData: (avatar, sink) {
if (avatar.contains(_avatarRegExp)) {
sink.add(avatar);
} else {
sink.addError('Enter a valid avatar photo');
}
});
}
and Bloc:
import 'dart:async';
import 'dart:io';
import 'validators.dart';
class Bloc extends Validators {
//instances
//
final _avatarPath = StreamController<File>();
final _name = StreamController<String>();
final _city = StreamController<String>();
//add data stream
Stream<File> get avatarPath => _avatarPath.stream;
Stream<String> get name => _name.stream.transform(validateName);
Stream<String> get city => _city.stream.transform(validateCity);
// change data
Function(File) get changeAvatarPath => _avatarPath.sink.add;
Function(String) get changeName => _name.sink.add;
Function(String) get changeCity => _city.sink.add;
//for cleanup
void dispose() {
_avatarPath.close();
_name.close();
_city.close();
}
}
// bloc.changeName---> bloc.nameController.sin.add
final bloc = new Bloc();

How to access all of child's state from Parent Widget in flutter?

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!

how to refresh state on Navigator.Pop or Push in flutter

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.