I am passing data from my alertdialog to item of listview.
Where I can find information about it?
I tried use TextEditingController, with Navigation Routes. And I used materials from this Tutorial
This is my code:
class MeasurementsScreen extends StatefulWidget {
#override
_MeasurementsScreenState createState() => _MeasurementsScreenState();
}
class _MeasurementsScreenState extends State<MeasurementsScreen> {
List<_ListItem> listItems;
String lastSelectedValue;
var name = ["Рост", "Вес"];
var indication = ["Введите ваш рост", "Введите ваш вес"];
TextEditingController customcintroller;
void navigationPageProgrammTrainingHandler() {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => ProgrammTrainingHandler()),
);
}
#override
void initState() {
super.initState();
initListItems();
}
Future<String> createAlertDialog(BuildContext context, int indexAl){
customcintroller = TextEditingController();
if(indexAl < 2){
return showDialog(context: context, builder: (context){
return AlertDialog(
title: Text(name[indexAl]),
content: TextField(
textDirection: TextDirection.ltr,
controller: customcintroller,
style: TextStyle(
color: Colors.lightGreen[400],
fontSize: 18.5),
decoration: InputDecoration(
contentPadding: EdgeInsets.only(bottom: 4.0),
labelText: indication[indexAl],
alignLabelWithHint: false,
),
keyboardType: TextInputType.phone,
textInputAction: TextInputAction.done,
),
actions: <Widget>[
FlatButton(
child: const Text('ОТМЕНА'),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: const Text('ОК'),
onPressed: () {
Navigator.of(context).pop(customcintroller.text.toString());
},
),
],
);
});
} else if (indexAl > 1){
navigationPageProgrammTrainingHandler();
}
}
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Color(0xff2b2b2b),
appBar: AppBar(
title: Text(
'Замеры',
style: new TextStyle(
color: Colors.white,
),),
leading: IconButton(
icon:Icon(Icons.arrow_back),
color: Colors.white ,
onPressed:() => Navigator.of(context).pop(),
),
),
body: ListView.builder(
physics: BouncingScrollPhysics(),
itemCount: listItems.length,
itemBuilder: (BuildContext ctxt, int index){
return GestureDetector(
child: listItems[index],
onTap: () {
createAlertDialog(context, index).then((onValue){
});
}
);
},
),
);
}
void initListItems() {
listItems = [
new _ListItem(
bgName: 'assets/images/soso_growth.jpg',
name: customcintroller.text.toString().isEmpty == false ? customcintroller.text.toString() : "Рост",
detail: "Нажми, чтобы добавить свой рост"),
new _ListItem(
bgName: 'assets/images/soso_weight.jpg',
name: customcintroller.text.toString().isEmpty == false ? customcintroller.text.toString() : "Вес",
detail: "Нажми, чтобы добавить свой вес"),
new _ListItem(
bgName: 'assets/images/soso_chest.jpg',
name: "Грудь",
detail: "PRO-версия"),
new _ListItem(
bgName: 'assets/images/soso_shoulder.jpg',
name: "Плечи",
detail: "PRO-версия"),
new _ListItem(
bgName: 'assets/images/soso_biceps.jpg',
name: "Бицепс",
detail: "PRO-версия")
];
}
}
class _ListItem extends StatelessWidget {
_ListItem({this.bgName, this.name, this.detail});
// final int index;
final String bgName;
final String name;
final String detail;
#override
Widget build(BuildContext context) {
return Container(
height: 180.0,
margin: const EdgeInsets.symmetric(
vertical: 1.0,
),
child: new Stack(
children: <Widget>[
new Container(
decoration: new BoxDecoration(
image: new DecorationImage(
image: new AssetImage(bgName),
colorFilter: new ColorFilter.mode(
Colors.black.withOpacity(0.45), BlendMode.darken),
fit: BoxFit.cover,
alignment: Alignment.center),
),
child: new SizedBox.expand(
child: Container(
alignment: Alignment.center,
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new Text(
name,
style: new TextStyle(fontSize: 29.0, color: Colors.white),
),
Padding(
padding: const EdgeInsets.only(top: 12.0),
child: new Text(
detail,
style:
new TextStyle(fontSize: 16.0, color: Colors.white),
),
)
],
),
),
),
),
],
),
);
}
}
I expect to get text from alertdialog.
Do you want to update data in your current screen or in another Scaffold/screen? Solution for the first option would be to set your desired data in a setState or pass it to a Model (with ScopedModel) and update the view. E.g. :
FlatButton(
child: const Text('ОТМЕНА'),
onPressed: () {
setState(() {
myData = data;
Navigator.of(context).pop();
}); // not sure if this will work, could you try?
}
),
If it is in a new/other screen, you can pass it in the Navigator like for example:
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NewScreen(data: myData),
),
);
},
Does this solve your problem? Otherwise, please elaborate on what it is you need.
I tried to use Navigator.of(context).pop("OK"); in the AlertDialog but it doesn't work.
use ValueChanged as a param on the showDialog method and it works well.
class Dialogs{
static showAlertDialog(BuildContext context, String title, String message,
{List<String> actions, ValueChanged onChanged}) async {
if (actions == null) {
actions = ["OK"];//default OK button.
}
await showDialog(
context: context,
child: AlertDialog(
title: Text(title ?? 'Message'),
content: Text(message),
actions: actions
.map((e) => new FlatButton(
child: Text(e.trim()),
onPressed: () {
Navigator.of(context).pop(e.trim());
if (onChanged != null) {
onChanged(e.trim());
}
}))
.toList(),
),
);
}
}
the caller looks like
_onProcess(){
String result = "";
await Dialogs.showAlertDialog(context, "Warning", "Warning message", actions: ["OK", "Cancel"],
onChanged: (value) {
result = value;
});
if (result != "OK") {
Dialogs.showSnackBarMessage(context, "Cancel This PTN");
return;
}
....process with the OK logic.
}
If you want to change an item of the list you can do that with setState() before calling Navigator.pop(). It is not possible to pass data through Navigator.pop() though, as you may be able to do with Navigator.push(... MyPage(data)).
You could achieve what you want through State management. You can check for state management tutorials in general. But the two practices that are used most in my opinion are Scoped Model and BLoC pattern. These practices help you pass data through the widget tree back and forward. I would recommend BLoC pattern there are many tutorials about it. Also there is a package which can be very helpful with BLoC pattern: flutter_bloc.
Related
Here is my main.dart code,
void main() {
// Bloc.observer = AppBlocObserver();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<SignInCubit>(
create: (BuildContext context) => SignInCubit(),
),
BlocProvider<SignUpCubit>(
create: (BuildContext context) => SignUpCubit(),
),
],
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return OrientationBuilder(
builder: (BuildContext context, Orientation orientation) {
//sizeConfiguration
SizeConfig().init(constraints, orientation);
return MaterialApp(
title: 'Flutter Demo',
theme: lightThemeData(context),
darkTheme: darkThemeData(context),
// themeMode: _themeMode,
scaffoldMessengerKey: Messenger.rootScaffoldMessengerKey,
navigatorKey: CustomRoutes.navigatorKey,
home: const SplashPage(),
initialRoute: SplashPage.id,
routes: <String, WidgetBuilder> {
SplashPage.id:(BuildContext context) => const SplashPage(),
SignInPage.id: (BuildContext context) => const SignInPage(),
SignUpPage.id: (BuildContext context) => const SignUpPage(),
},
);
});
}),
);
}
Here is the code on SignIn screen,
class SignInPage extends StatefulWidget {
const SignInPage({super.key});
static const String id = 'SignInPage';
#override
State<SignInPage> createState() => _SignInPageState();
}
class _SignInPageState extends State<SignInPage> {
final dynamic _formKey = GlobalKey<FormState>();
final TextEditingController _passwordController = TextEditingController();
#override
void initState() {
createFocusNodeListeners();
SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
_passwordFocus.addListener(_passwordListener);
});
super.initState();
}
#override
Widget build(BuildContext context) {
final CTTheme cTTheme = Theme.of(context).extension<CTTheme>()!;
//https://medium.com/codex/flutter-bloc-an-introduction-with-cubit-7eae1e740fd0
final SignInCubitModel state = context.watch<SignInCubit>().state;
return Scaffold(
body: CustomPaint(
painter:
LoginCustomPaint(customPaintColor: cTTheme.customPaintCircleColor1),
child: GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24), // common padding
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(
height: 98,
),
Text(
AppStrings.welcomeTo, // heading 3
style: cTTheme.heading3,
),
const SizedBox(
height: 16,
),
RichText(
text: TextSpan(
text: AppStrings.m,
style: cTTheme.heading1, // heading1 with pink color
children: <TextSpan>[
TextSpan(
text: AppStrings.onteFiore,
style: cTTheme.heading2, //heading2 with pink color
),
TextSpan(
text: '\n${AppStrings.c}', style: cTTheme.heading1),
TextSpan(
text: AppStrings.linical, style: cTTheme.heading2),
TextSpan(
text: ' ${AppStrings.t}', style: cTTheme.heading1),
TextSpan(
text: AppStrings.rials, style: cTTheme.heading2),
],
),
),
const SizedBox(
height: 16,
),
Text(
AppStrings.findOutClinicalTrailAndStudies,
style: cTTheme.subtitle3, // subtitle 1
),
SizedBox(
height: SizeConfig.heightMultiplier! * 19.8,
),
TextFormField(
autofocus: false,
style:cTTheme.menuItemTextStyle,
controller: _passwordController,
autovalidateMode: state.passwordValidation
? AutovalidateMode.onUserInteraction
: AutovalidateMode.disabled,
obscureText: !state.signInPasswordVisibility,
// obscuringCharacter: '*',
focusNode: _passwordFocus,
decoration: kTextFieldDecoration2(context).copyWith(
labelText: AppStrings.password,
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: state.isInvalidPassWord
? state.passWordTextFormFieldColor
: cTTheme.dividerColor!)),
prefixIcon: Padding(
padding: const EdgeInsetsDirectional.only(end: 16.0),
child: Image.asset(// constants
AppIconsPath.padLockIcon),
),
suffixIcon: InkWell(
onTap: () {
context
.read<SignInCubit>()
.changeSignINPassowrdVisibilty(
state.signInPasswordVisibility
? ChangePasswordVisibilityEnum.invisible
: ChangePasswordVisibilityEnum.visible);
},
child: state.signInPasswordVisibility
? Image.asset(AppIconsPath.eyeIcon)
: Image.asset(AppIconsPath.privacyIcon),
),
),
onTap: () => context
.read<SignInCubit>()
.changePasswordTextFormFieldColor(
color: cTTheme.dividerColor!,
isInvalidPassWord: false),
onChanged: (String? value) {
_debouncer.run(() {
checkControllerValues();
});
},
onEditingComplete: () {
if (Validators.validateSignInPassword(
_passwordController.text) !=
null) {
context
.read<SignInCubit>()
.enableValidatorForPasswordTextField();
}
},
validator: (String? value) =>
Validators.validateSignInPassword(value!),
),
errorMessage(state.isInvalidPassWord, 'Password Is Incorrect'),
const SizedBox(
height: 14,
),
Align(
alignment: Alignment.bottomRight,
child: InkWell(
onTap: () async {
final bool isConnocted = await checkConnectivity();
if (isConnocted) {
CustomRoutes.push(
screen: const ForgotPasswordPage());
return;
}
CustomRoutes.push(
screen: InternetErrorScreen(
onPressed: () {
ConnectivityManager.internetChecker()
.then((bool retry) {
if (retry) {
CustomRoutes.back();
}
});
},
),
);
},
child: Text(
AppStrings.forgotPassword,
style: cTTheme.description1,
))),
const SizedBox(
height: 48,
),
PrimaryButton(
onPressed: state.isSignInButtonDissabled
? null
: () {
FocusManager.instance.primaryFocus?.unfocus();
if (_formKey.currentState!.validate()) {
context.read<SignInCubit>().onSignInSubmit(
_emailOrPhoneController.text,
_passwordController.text);
}
},
text: AppStrings.signIn.toUpperCase(),
isLoadingVisible: state.isLoading,
),
const SizedBox(
height: 16,
),
InkWell(
onTap: () async {
final bool isConnocted = await checkConnectivity();
if (isConnocted) {
Navigator.pushNamed(context, SignUpPage.id);
// CustomRoutes.pushreplace(screen: const SignUpPage());
return;
}
CustomRoutes.push(
screen: InternetErrorScreen(onPressed: () {
ConnectivityManager.internetChecker()
.then((bool retry) {
if (retry) {
CustomRoutes.back();
}
});
}));
},
child: Center(
child: RichText(
text: TextSpan(
text: AppStrings.dontHaveAnAccount,
style: cTTheme.subtitle2,
children: <TextSpan>[
TextSpan(
text: ' ${AppStrings.signUp}',
style: cTTheme.iconButtonTextStyle),
],
),
),
),
),
const SizedBox(
height: 66,
),
Center(
child: Text(
AppStrings.byLoggingInYouAgreeToOur,
style: cTTheme.noteTextStyle1,
)),
const SizedBox(
height: 6,
),
Center(
child: InkWell(
onTap: () => CustomRoutes.push(
screen: InternetErrorScreen(onPressed: () {
ConnectivityManager.internetChecker().then((bool value) {
if (value) {
CustomRoutes.back();
}
});
})),
child: Text(
AppStrings.termsAndpolicies,
style: cTTheme.noteTextStyle2,
),
)),
const SizedBox(
height: 20,
),
],
),
),
),
),
),
));
}
I am switching(show/hide) eye icon on the password textfield to show and hide the eye image using "changeSignINPassowrdVisibilty" function from cubit file. Here is the Cubit file code,
class SignInCubit extends Cubit<SignInCubitModel> {
SignInCubit()
: super(
SignInCubitModel()); // here inside SignInCubit class, all the fields are already initialized
final CTTheme cTTheme =
Theme.of(CustomRoutes.navigatorKey.currentContext!).extension<CTTheme>()!;
void changeSignINPassowrdVisibilty(ChangePasswordVisibilityEnum newState) {
emit(state.copyWith(
signInPasswordVisibility:
newState == ChangePasswordVisibilityEnum.visible));
}
}
Here is the code from SignInCubitModel class,
class SignInCubitModel {
SignInCubitModel({
this.signInPasswordVisibility = false,
});
bool signInPasswordVisibility;
SignInCubitModel copyWith({
bool? signInPasswordVisibility,
}) {
return SignInCubitModel(
signInPasswordVisibility:
signInPasswordVisibility ?? this.signInPasswordVisibility,
);
}
}
Initially signInPasswordVisibility field value is false. When user try to switch visibility of the password , signInPasswordVisibility will change its value to true or false.
Here is my problem,
1.I am enabling signInPasswordVisibility to true on SignIn screen by clicking eye icon. Please note now signInPasswordVisibility value is true.
And
2.I am pushing to Signup screen when they click signup button from SignIn screen.
And
3.I am pushing to SignIn screen again when they click SignIn button from signup screen.
But this time signInPasswordVisibility value in SignIn screen still remains true. Its supposed to be false since I am pushing to this screen again. It has to reset the all SignIn screen state values when I push to SignIn screen. But Its not happening.
Signin screen still keeps old state values even if I push to the Signin screen multiple time.
Is there any problem with the way I implemented flutter_bloc? OR Is there any solution to reset the state values every time we push the SignIn screen?
I'm trying to change the value of the text which is the 'placeholder' in this method. But for some reason it's not updating to the new value that i wrote & retrieved from the dialog. I even tested to see what's the value of 'placeholder' and it returned the correct value/text that i wrote in the dialog but still it wasn't updated in the card.
Widget buildTextField(
String subtitle, String placeholder,icon) {
// ignore: dead_code
return Padding(
padding: const EdgeInsets.only(bottom: 15.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
color: Colors.white,
margin: EdgeInsets.fromLTRB(100, 10, 100, 10),
child: ListTile(
title: Text(placeholder),
subtitle: Text(subtitle),
leading: Icon(
icon,
color: Color.fromARGB(255, 44, 148, 233),
),
trailing: IconButton(
onPressed: () async {
final name = await openDialog(subtitle,placeholder);
if(name == null || name.isEmpty) return;
print(name);
setState(() => placeholder = name);
print(placeholder);
//this.title: Text(placeholder);
// setState(() {
// placeholder = this.name;
// });
},
icon: Icon(Icons.edit_outlined),
), ),
),
);
}
Future <String?>openDialog(String subtitle, String placeholder) => showDialog<String>
(
context: context, builder: (context)=> AlertDialog(
title: Text(subtitle),
content: TextField(autofocus: true,
decoration: InputDecoration(hintText: 'Enter your name'),
controller: controller ..text = placeholder,
// controller: controller,
),
actions: [
TextButton(onPressed: submit,
child: Text('Save'))
],
),
);
void submit(){
// Navigator.of(context).pop(controller.text);
Navigator.of(context, rootNavigator: true).pop(controller.text);
}
The text don't update in your card because when your setState rebuild your widget tree, the call to the function buildTextField reset it to the value passed in parameter. You can try to use a variable defined in your class instead of using an Hard coded string in your function parameter.
Here an exemple:
class _MyHomePageState extends State<MyHomePage> {
TextEditingController controller = TextEditingController();
String CardText = "Name";
String CardSubtitle = 'Card Subtitle';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
backgroundColor: Color.fromRGBO(37, 57, 92, 1.0),
),
body: SafeArea(
child: Center(
child: Column(
children: [
buildTextField(
CardSubtitle, CardText, Icons.person
),
],
),
),
),
);
}
Widget buildTextField(
String subtitle, String placeholder,icon) {
// ignore: dead_code
return Padding(
padding: const EdgeInsets.only(bottom: 15.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
color: Colors.white,
margin: EdgeInsets.fromLTRB(100, 10, 100, 10),
child: ListTile(
title: Text(placeholder),
subtitle: Text(subtitle),
leading: Icon(
icon,
color: Color.fromARGB(255, 44, 148, 233),
),
trailing: IconButton(
onPressed: () async {
final name = await openDialog(subtitle,placeholder);
if(name == null || name.isEmpty) return;
print(name);
setState(() => CardText = name);
print(placeholder);
//this.title: Text(placeholder);
// setState(() {
// placeholder = this.name;
// });
},
icon: Icon(Icons.edit_outlined),
), ),
),
);
}
Future <String?>openDialog(String subtitle, String placeholder) => showDialog<String>
(
context: context, builder: (context)=> AlertDialog(
title: Text(subtitle),
content: TextField(autofocus: true,
decoration: InputDecoration(hintText: 'Enter your name'),
controller: controller ..text = placeholder,
// controller: controller,
),
actions: [
TextButton(onPressed: submit,
child: Text('Save'))
],
),
);
void submit(){
// Navigator.of(context).pop(controller.text);
Navigator.of(context, rootNavigator: true).pop(controller.text);
}
}
Try to wrap your Card widget with the Builder Widget that can rebuild and update your widget
Builder(
builder: (context) {
return Card(...);
},
),
I will describe my coding problem.
Well I have created Login/signUp .dart files(pages) but while debugging I got this error...
The method 'showSnackBar' isn`t defined for the type 'ScaffoldState'.
This error message appears everywhere in .dart code where I have this coding line...
`
scaffoldKey.currentState.showSnackBar(snackbar);
`
Here below is full code of my "login_page.dart" file...
`
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
void showSnackBar(String title) {
final snackbar = SnackBar(
content: Text(
title,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 15),
),
);
scaffoldKey.currentState.showSnackBar(snackbar);
}
var emailIdController = TextEditingController();
var passwordController = TextEditingController();
Widget _buildLogin() {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 30),
child: Column(
children: [
InputTextField(
controller: emailIdController,
label: 'Email-Id',
icon: Icon(Icons.email_outlined),
),
InputTextField(
controller: passwordController,
label: 'Password',
icon: Icon(Icons.lock),
),
SizedBox(
height: 50,
),
GestureDetector(
onTap: () async {
// network connectivity
var connectivityResult = await Connectivity().checkConnectivity();
if (connectivityResult != ConnectivityResult.mobile &&
connectivityResult != ConnectivityResult.wifi) {
showSnackBar('No Internet connectivity');
return;
}
if (!emailIdController.text.contains('#')) {
showSnackBar('Please provide a valid email address');
}
if (passwordController.text.length < 6) {
showSnackBar('Please provide a password of length more than 6');
}
BuildContext dialogContext;
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
dialogContext = context;
return ProgressDialog(
status: 'Logging you in...',
);
},
);
context
.read<AuthenticationService>()
.signIn(
email: emailIdController.text.trim(),
password: passwordController.text.trim(),
)
.then((value) => Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return HomePage();
}),
));
Navigator.pop(dialogContext);
},
child: CustomButton(
text: 'Login',
),
),
Text("\nDon't have any account?"),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return SignUpPage();
}),
);
},
child: Text(
'SignUp here',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
),
),
SizedBox(
height: 20,
),
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
body: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Padding(
padding: EdgeInsets.only(top: 130),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
'hopOn',
style: TextStyle(
fontSize: 60,
letterSpacing: 2,
fontWeight: FontWeight.bold,
fontFamily: 'MuseoModerno',
// color: Colors.white,
),
),
SizedBox(height: 80),
Padding(
padding: EdgeInsets.symmetric(horizontal: 30),
child: Column(
children: [
InputTextField(
controller: emailIdController,
label: 'Email-Id',
obscure: false,
icon: Icon(Icons.email_outlined),
),
InputTextField(
controller: passwordController,
label: 'Password',
obscure: true,
icon: Icon(Icons.lock),
),
SizedBox(
height: 30,
),
GestureDetector(
onTap: () async {
// network connectivity
var connectivityResult =
await Connectivity().checkConnectivity();
if (connectivityResult != ConnectivityResult.mobile &&
connectivityResult != ConnectivityResult.wifi) {
showSnackBar('No Internet connectivity');
return;
}
if (!emailIdController.text.contains('#')) {
showSnackBar(
'Please provide a valid email address');
}
if (passwordController.text.length < 6) {
showSnackBar(
'Please provide a password of length more than 6');
}
BuildContext dialogContext;
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
dialogContext = context;
return ProgressDialog(
status: 'Logging you in...',
);
},
);
context
.read<AuthenticationService>()
.signIn(
email: emailIdController.text.trim(),
password: passwordController.text.trim(),
)
.then((value) => Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return HomePage();
}),
));
Navigator.pop(dialogContext);
},
child: CustomButton(
text: 'Login',
),
),
SizedBox(
height: 20,
),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return SignUpPage();
}),
);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Don't have any account?\t",
style: TextStyle(fontSize: 10),
),
Text(
'SignUp here',
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 12),
),
],
),
),
SizedBox(
height: 20,
),
],
),
),
],
),
),
),
),
);
}
}
`
The problem from whole code appears in this class...
`
void showSnackBar(String title) {
final snackbar = SnackBar(
content: Text(
title,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 15),
),
);
scaffoldKey.currentState.showSnackBar(snackbar); // <=== here is problem, this line have error message --> The method 'showSnackBar' isn`t defined for the type 'ScaffoldState'.
}
`
Any help is appreciated.
Thank you!
I would like to fix that "snackBar" error problem.
I tryed several hours to fix it by myself but without succees.
Flutter uses below method to display the snackbar as per flutter.dev
ScaffoldMessenger.of(context).showSnackBar(snackBar);
I found a solution idea that other user named "Rohan" gave to me.
I changed problematic code line...
scaffoldKey.currentState.showSnackBar(snackbar);
to first solution (which still didn't worked,, appeared error "Undefined name: 'snackbar'")...
ScaffoldMessenger.of(context).showSnackBar(snackBar);
Didn`t worked, but then I found a solution from previous sample solution. This is general and working Solution for my problem...
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: snackbar));
This solution will help you, if you found the same or similar issue with "showSnackBar".
The task text in edit_todo_screen is not updated. I'm using the same code as in item_tasks, where I change the status of the task to move between "Done" and "Archived" - everything works well here. I tried to change only the status in edit_todo_screen, but it does not change, although the code is identical to the code in item_tasks. Perhaps the problem is that I'm not passing the parameters correctly to edit_todo_screen. I need to be able to change the status of the task and the text of the task itself in edit_todo_screen. Attached below is a screenshot of the error that occurs when clicking the button in edit_todo_screen
Tell me, please, what could be my mistake?
cubit_db
class AppCubit extends Cubit<AppStates> {
AppCubit() : super(AppInitialState());
static AppCubit get(context) => BlocProvider.of(context);
void updateDatabase(String status, int id) async {
database!.rawUpdate(
'UPDATE tasks SET status = ? WHERE id = ?', [status, id]).then((value) {
getDataBase(database);
emit(AppUpdateDatabaseState());
});
}
void createDatabase() {
openDatabase(
'todo.db',
version: 1,
onCreate: (database, version) {
database
.execute(
'CREATE TABLE tasks (id INTEGER PRIMARY KEY, title TEXT, status TEXT)')
.then((value) => print('Table Created'))
.catchError((error) {
print('Error When Creating Table ${error.toString()}');
});
},
onOpen: (database) {
getDataBase(database);
print('database opened');
},
).then((value) {
database = value;
emit(AppCreateDatabaseState());
});
}
inserToDatabase({required String title}) async {
await database!.transaction((txn) async {
txn
.rawInsert(
'INSERT INTO tasks (title, status) VALUES ("$title","New")')
.then((value) {
getDataBase(database);
print('$value Inserted Successfully');
emit(AppInsertDatabaseState());
}).catchError((error) {
print('Error When inserting Table ${error.toString()}');
});
});
}
new_tasks_list
class NewTasksScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocConsumer<AppCubit, AppStates>(
listener: (context, state) {},
builder: (context, state) {
var tasks = AppCubit.get(context).newTasks;
return SingleChildScrollView(
child: Column(children: [
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: tasks.length,
itemBuilder: (context, index) => TaskItem(tasks: tasks[index]),
),
]),
);
},
);
tasks_item
class TaskItem extends StatelessWidget {
Map? tasks;
TaskItem({this.tasks});
#override
Widget build(BuildContext context) {
return Card(
key: Key(tasks!['title']),
shadowColor: Colors.blueGrey,
margin: const EdgeInsets.only(left: 15, right: 15, top: 8),
color: Colors.black,
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.grey.shade800, width: 0.5),
borderRadius: BorderRadius.circular(10),
),
borderOnForeground: false,
child: ListTile(
title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(
// '${state.loadedUser[index].description}',
tasks!['title'],
style: const TextStyle(
fontSize: 21.0,
// fontWeight: FontWeight.bold,
),
),
// Text(
// tasks!['status'],
// style: const TextStyle(fontSize: 21.0),
// ),
]),
trailing: IconButton(
tooltip: 'Archive Todo',
highlightColor: Colors.red,
onPressed: () {
AppCubit.get(context).updateDatabase('Archive', tasks!['id']);
},
icon: const Icon(
Icons.archive,
color: Colors.white,
),
),
leading: IconButton(
tooltip: 'Done Todo',
highlightColor: Colors.green,
onPressed: () {
AppCubit.get(context).updateDatabase('Done', tasks!['id']);
},
icon: const Icon(
Icons.check,
color: Colors.white,
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EditTodoScreen(
title: tasks!['title'],
id: tasks!['id'],
),
),
);
},
),
);
}
}
edit_todo_screen
class EditTodoScreen extends StatelessWidget {
// Map? tasks;
String title;
int id;
EditTodoScreen({Key? key, required this.title, required this.id})
: super(key: key);
final _controller = TextEditingController();
#override
Widget build(BuildContext context) {
_controller.text = title;
return BlocConsumer<AppCubit, AppStates>(
listener: (context, state) {},
builder: (context, state) {
return Scaffold(
appBar: AppBar(
title: const Text(
'Edit Todo',
style: TextStyle(fontSize: 20.0),
),
),
body: _body(context),
);
});
}
Widget _body(context) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
TextFormField(
controller: _controller,
autocorrect: true,
maxLines: 2,
decoration: const InputDecoration(hintText: 'Enter todo message'),
),
const SizedBox(
height: 10.0,
),
// ElevatedButton(
// // style:,
// onPressed: () {
// AppCubit.get(context).updateDatabase('Done', id);
// },
// child: Text(
// 'Update Data',
// style: TextStyle(color: Colors.amber.shade700),
// ),
// ),
InkWell(
onTap: () {
AppCubit.get(context).updateDatabase('Done', id);
Navigator.pop(context);
},
child: _updateBtn(context),
)
],
),
);
}
Widget _updateBtn(context) {
return Container(
width: MediaQuery.of(context).size.width,
height: 50.0,
decoration: BoxDecoration(
color: Colors.black, borderRadius: BorderRadius.circular(10.0)),
child: Center(
child: Text(
'Update Todo',
style: TextStyle(
fontSize: 17.0,
color: Colors.amber.shade700,
fontWeight: FontWeight.bold),
),
),
);
}
}
I think your problem has to do with the fact that database is not set in the second case. The code fails because you try to access a null value that then is checked with the "!" operator. Look where you set the database and check if that code is called in both code flows.
edit:
I think this line in edit todo screen is your problem: AppCubit.get(context).updateDatabase('Done', id);. If I am not mistaken AppCubit.get(context) returns null. The easiest way to check if I am right would be to replace it with the following:
final appCubit = AppCubit.get(context);
print('$appCubit');
appCubit.updateDatabase('Done', id);
If I am right, you should see "null" in your terminal.
What I think happens is, that the app cubit is not provided in the context anymore, because you pushed the todo screen as a new screen on the navigator. With that, the context that is provided is that of the navigator (or probably the material app in your case) which is above the point where you provide the AppCubit.
I am kind of guessing though, because I only see half of your code. I hope it helps nevertheless. :)
what I want to achieve is to change the image in CircleAvatar when I'm selecting an image, here is the code:
ProfileController:
class ProfileController extends GetxController {
final TextEditingController emailController = TextEditingController();
final ImagePicker _picker = ImagePicker();
Rx<String?> avatarPath = null.obs;
avatarFromCamera() async {
var localAvatar = await _picker.pickImage(
source: ImageSource.camera, imageQuality: 50
);
if (localAvatar != null) {
avatarPath = localAvatar.path.obs;
update();
}
}
avatarFromGallery() async {
var localAvatar = await _picker.pickImage(
source: ImageSource.gallery, imageQuality: 50
);
if (localAvatar != null) {
avatarPath = localAvatar.path.obs;
update();
}
}
String? emailValidator(String? value) {
if (value == null || value.isEmpty) {
return null;
}
if (!EmailValidator.validate(value, false)) {
return 'Invalid email address';
}
}
#override
void onClose() {
emailController.dispose();
super.onClose();
}
String? emailValidator(String? value) {
if (value == null || value.isEmpty) {
return null;
}
if (!EmailValidator.validate(value, false)) {
return 'Invalid email address';
}
}
void save(GlobalKey<FormState> profileFormKey) {
if (profileFormKey.currentState!.validate()) {
print('valid');
}
}
}
and here is the ProfileScreen widget:
lass ProfileScreen extends StatelessWidget {
final ProfileController _profileController = Get.put<ProfileController>(ProfileController());
GlobalKey<FormState> profileFormKey = GlobalKey<FormState>();
ProfileScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Update user details'),
),
body: SingleChildScrollView(
child: Form(
key: profileFormKey,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(30.0),
child: TextFormField(
keyboardType: TextInputType.text,
controller: _profileController.emailController,
decoration: const InputDecoration(
labelText: 'Enter email',
),
validator: _profileController.emailValidator,
),
),
Center(
child: GestureDetector(
onTap: () {
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return SafeArea(
child: Wrap(
children: <Widget>[
ListTile(
leading: const Icon(Icons.photo_library),
title: const Text('Photo Library'),
onTap: () {
_profileController.avatarFromGallery();
Navigator.of(context).pop();
}),
ListTile(
leading: const Icon(Icons.photo_camera),
title: const Text('Camera'),
onTap: () {
_profileController.avatarFromCamera();
Navigator.of(context).pop();
},
),
],
),
);
}
);
},
child: CircleAvatar(
radius: 55,
backgroundColor: Colors.pink,
child: Obx(() =>(_profileController.avatarPath.value != null)
? ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.file(
File(_profileController.avatarPath.value!),
width: 100,
height: 100,
fit: BoxFit.fitHeight
),
)
: Container(
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(50)),
width: 100,
height: 100,
child: Icon(
Icons.camera_alt,
color: Colors.grey[800],
),
),
),
),
)),
Container(
margin: const EdgeInsets.all(10),
width: double.infinity,
child: MaterialButton(
color: Colors.blue,
onPressed: () => _profileController.save(profileFormKey),
child: const Text(
'Submit',
style: TextStyle(color: Colors.white),
),
),
),
],
),
),
),
);
}
}
as you can see, I have Obx and my avatarPath reactive, and I'm running update everywhere I changing it, but it's not udpated. I also tried to use empty string as initial value of imagePath like this Rx<String> avatarPath = ''.obs; and it's not working. What I'm doing wrong??? Thank you in advice!!!
There are two things to revise for it. Firstly, change avatarPath = localAvatar.path.obs; with avatarPath.value = localAvatar.path;. Because localAvatar.path.obs create the new observable and changes will not be reflected to previous observers.
Secondly, create a new stateless widget having the widget tree of bottom sheet's builder like
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return CustomBottomView();
}
);
Then inside the CustomBottomView copy your bottom sheet widget tree.
class CustomBottomView extends GetView<ProfileController> {
return YourBottomSheetWidgetTreeHere();
}
Dont worry about ProfileController here. You have already put it in DI in previous route. First follow the first step if you still face the problem second step will definitely resolve it.