Data not updating in DB Sqflite Flutter - flutter

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. :)

Related

flutter_bloc is not resetting its Cubit Model values when we try to push the same screen more than one time

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?

how to update the value of a text in a card - Flutter/dart

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(...);
},
),

When I tab on Icon, its color changes only for seconds and disappears in Futter

So I have problem with the changing color of an icon when it tabbed
thus, when I tab on icon it's only changes for its prober colors but for seconds and then disappears
I have used Provider as shown in the below code, and I also used isChecked = true but when I tab on one icon, all of them change as well.
So what should I do with this problem?
this this the code but my problem remains in Icon Button which's will be below it
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:note/Designs/Favorite_List.dart';
import 'package:note/Models/Favorite_List_Provider.dart';
import 'package:note/Models/Food_Models.dart';
import 'package:provider/provider.dart';
import '../Services/Fetch_Data.dart';
import 'Details_DesignOfDesignOne.dart';
class DesignOne extends StatefulWidget {
const DesignOne({super.key, required this.Image, required this.Desc});
final String Image;
final String Desc;
#override
State<DesignOne> createState() => _DesignOneState();
}
class _DesignOneState extends State<DesignOne> {
late Future<Food> futureFood;
#override
void initState() {
super.initState();
futureFood = fetchData(widget.Image, widget.Desc);
}
bool ischecked = false;
#override
Widget build(BuildContext context) {
final provider = favorite.of(context);
return Scaffold(
backgroundColor: Colors.grey.shade200,
appBar: AppBar(
title: Text('Design one'),
actions: [
Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FavoriteScreen(Desc: '', Image: ''),
),
);
},
child: Icon(
Icons.list,
size: 30,
),
),
)
],
),
body: Consumer<favorite>(
builder: (BuildContext context, favorite value, child) {
return Center(
child: FutureBuilder<Food>(
future: fetchData(widget.Image, widget.Desc),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.categories.length,
itemBuilder: (contxt, index) {
final fav = snapshot.data!.categories[index];
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailsDo(
Desc: snapshot.data!.categories[index]
.strCategoryDescription,
Image: snapshot
.data!.categories[index].strCategoryThumb,
),
),
);
},
child: Card(
elevation: 2,
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.white70, width: 1),
borderRadius: BorderRadius.circular(50),
),
child: ListTile(
title: Text(
snapshot.data!.categories[index].strCategory
.toString(),
style: GoogleFonts.montserrat(
fontSize: 20,
fontWeight: FontWeight.w600,
fontStyle: FontStyle.italic,
),
),
leading: CircleAvatar(
backgroundColor:
Color.fromARGB(213, 255, 251, 251),
child: Text(
snapshot.data!.categories[index].idCategory
.toString(),
style: GoogleFonts.montserrat(
fontSize: 20,
fontWeight: FontWeight.w600,
fontStyle: FontStyle.italic,
color: Color.fromARGB(255, 148, 148, 135)),
),
),
trailing: IconButton(
icon: (provider.isExist(fav) && ischecked)
? const Icon(Icons.favorite,
color: Colors.red)
: const Icon(Icons.favorite_border),
onPressed: () {
provider.toggleFavorite(fav);
setState(() {
ischecked = !ischecked;
});
},
),
),
),
);
});
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
),
);
}),
);
}
}
and this is the specific problem with the Icon Button :
trailing: IconButton(
icon: (provider.isExist(fav) && ischecked)
? const Icon(Icons.favorite,
color: Colors.red)
: const Icon(Icons.favorite_border),
onPressed: () {
provider.toggleFavorite(fav);
setState(() {
ischecked = !ischecked;
});
},
),
And here is the problem :
Would you try this way instead of isChecked variable??
class _DesignOneState extends State<DesignOne>{
static late List<bool> isChecked;
///*** your code ****
Widgetbuild(BuildContext context){
///*** your code ***
if(snapshot.hasData){
isChecked = List.filled(snapshot.data!.categories.length,false);
/// *** your code ***
icon: (provider.isExist(fav) && isChecked[index])
/// *** your code ***
setState((){
isChecked[index] = !isChecked[index];
});
}
}
}

I cannot update UI state with riverpod

I'm working on a quiz app as a personal project and what I want to do is make it possible for the user to name a question set. (Kind of like a folder for questions on a particular subject). I am using Riverpod. (I've worked with the provider package a couple of times) for state management but it seems I've missed a step or two because when I type in the name, I don't see it on the page. I hope I can be pointed in the right direction. Thanks
Class forRiverpod model which shows a list of type QuestionSetConstructor for taking the title. There is also a method for accepting the question title to add to the list
class RiverpodModel extends ChangeNotifier {
final List<QuestionSetConstructor> _questionSetList = [];
UnmodifiableListView<QuestionSetConstructor> get questionSet {
return UnmodifiableListView(_questionSetList);
}
void addTitleT(String title) {
final addQuestionTitle = (QuestionSetConstructor(title: title));
_questionSetList.add(addQuestionTitle);
notifyListeners();
}
int get count{
return questionSet.length;
}
}
`
This is for the alert dialog that will take the question title.
In the elevated button, I stated that I want the contents of the
text field to be added to the list in the Riverpod model.
void setQuestionNameMethod(context) {
showDialog(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return SetQuestionNameAlertDialog();
});
}
class SetQuestionNameAlertDialog extends ConsumerStatefulWidget {
#override
_SetQuestionNameAlertDialogState createState() =>
_SetQuestionNameAlertDialogState();
}
class _SetQuestionNameAlertDialogState
extends ConsumerState<SetQuestionNameAlertDialog> {
final TextEditingController questionNameController = TextEditingController();
final riverPodModelProvider2 =
ChangeNotifierProvider((ref) => RiverpodModel());
#override
Widget build(
BuildContext context,
) {
final questionNameRef = ref.watch(riverPodModelProvider2);
return AlertDialog(
title: Text("Name of Question Set",
style: TextStyle(
color: Colors.blue[400],
fontSize: 20,
fontWeight: FontWeight.w600)),
content: TextField(
controller: questionNameController,
),
actions: [
Center(
child: ElevatedButton(
onPressed: () {
setState(() {
questionNameRef.addTitleT(questionNameController.text) ;
});
print(questionNameRef.questionSet.first.title);
Navigator.pop(context);
},
child: const Text("Save"))),
],
);
}
}
`
This is the page where the question title is shown as a list. However, for some reason it is not showing.
class QuestionSetPage extends ConsumerStatefulWidget {
#override
_QuestionSetPageState createState() => _QuestionSetPageState();
}
class _QuestionSetPageState extends ConsumerState<QuestionSetPage> {
final riverPodModelProvider =
ChangeNotifierProvider((ref) => RiverpodModel());
#override
Widget build(BuildContext context) {
final questionSetRef = ref.watch(riverPodModelProvider);
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.grey[50],
centerTitle: true,
actions: [
IconButton(
onPressed: () {
setState(() {
setQuestionNameMethod(context);
// modalSheetMethod(context);
});
},
icon: Icon(
Icons.add,
size: 25,
color: Colors.blue[400],
))
],
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Question Set List",
style: TextStyle(
color: Colors.blue[400],
fontSize: 30,
fontWeight: FontWeight.w600),
),
),
Expanded(
child: ListView.builder(
itemCount: questionSetRef.count,
itemBuilder: (BuildContext context, int index) {
return Tiles(
title: questionSetRef.questionSet[index].title,
);
}),
)
],
),
);
}
}
class Tiles extends StatelessWidget {
String title;
Tiles({required this.title});
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(15, 3, 15, 3),
child: Material(
elevation: 2,
child: GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return QuestionSetsQuestionPage();
}));
},
child: ListTile(
title: Text(
title,
style: TextStyle(
color: Colors.blue[400],
fontSize: 17,
fontWeight: FontWeight.w400),
),
tileColor: Colors.white,
// subtitle: Text(
// "${questionSets[index].numberOfQuestions} number of questions",
// ),
leading: const Icon(
Icons.add_box_outlined,
size: 30,
),
),
),
),
);
}
}

How to pass data from alertdialog to page in flutter?

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.