Transferring a dart setter around in flutter - flutter

I am working on creating a signup process with multiple screens. For now I have first name and age screen. I have the following usermodel. The idea is whenever I update one of the field, it triggers rebuild of the signup-screen, so that the next item in sign-up is shown. I have 2 questions:
Can I pass around the setter from the user model, like I did below. Or is there a better way.
I am getting the following error when I click the 'Next_na' button inorder to add the first name
LateInitializationError: Field 'firstName' has not been initialized.
Thank you for your help in advance!
class UserModel extends ChangeNotifier {
late String firstName;
late int userAge;
int indexer = 0;
set addFName(String firstName) {
firstName = this.firstName;
indexer = indexer + 1;
notifyListeners();
}
String get fName {
return firstName;
}
set addUAge(int userAge) {
userAge = this.userAge;
indexer = indexer + 1;
notifyListeners();
}
int get uAge {
return userAge;
}
}
The signup screen is as follows
#override
Widget build(BuildContext context) {
return Scaffold(
body: ChangeNotifierProvider(
create: (context) => UserModel(),
child: Center(
child: Padding(
padding: const EdgeInsets.all(18.0),
child: Consumer<UserModel>(builder: (context, user, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"Sign Up",
style: TextStyle(fontSize: 24),
textAlign: TextAlign.center,
),
const Divider(
height: 30,
endIndent: 20,
indent: 20,
thickness: 1.5,
color: Colors.grey,
),
const SizedBox(
height: 30,
),
if (user.indexer == 0)
FirstNameWidget(
user: user,
)
else
FirstNameWidget(
user: user,
)
],
);
}),
)),
));
}
and the 'firstNameWidget' that shows the first name field and the 'next' button is as follows:
class FirstNameWidget extends StatelessWidget {
FirstNameWidget({Key? key, required this.user}) : super(key: key);
final UserModel user;
final firstNameController = TextEditingController();
#override
Widget build(BuildContext context) {
return Form(
//key: _firstNameFormKey,
child: Column(
children: [
TextFormField(
keyboardType: TextInputType.text,
controller: firstNameController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter First Name',
),
//autovalidateMode: AutovalidateMode.onUserInteraction,
),
const SizedBox(
height: 20,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.8,
child: ElevatedButton(
onPressed: () {
print(firstNameController.text);
commitFirstName(fName: firstNameController.text, user: user);
//user.addFName = firstNameController.text;
},
child: const Text('Next_na'),
),
),
],
),
);
}
}
void commitFirstName({required String fName, required UserModel user}) {
user.addFName = fName;
}

You did a mistake in your setter:
firstName = this.firstName;
You assign the class member (which has not been initialized yet) to your firstName param which comes from the TextEditingController.
If you turn this around it should work:
this.firstName = firstName;

Related

Filter search filter data showing from Api using provider

how to implement search filter with API using provider? i have post Api when user hit Api it send back data to the user
My api
Future<List<Searchmodel>>Search(String keyword) async {
final response=await http.post(Uri.parse('https://example.com/api/library/search'),body: {
'Keyword': keyword,
});
if(response.statusCode==200){
final json=response.body..toString().replaceAll("\n","");
var data=jsonDecode(json);
final Iterable fetch = data["success"];
return fetch.map((category) => Searchmodel.fromJson(category)).toList();
}else{
throw Exception("Unable to perform request!");
}
}
//model class
class Searchmodel {
Searchmodel({
required this.id,
required this.name,
required this.file,
required this.categoryId,
required this.subcategoryId,
required this.userId,
required this.status,
required this.createdAt,
required this.updatedAt,});
Searchmodel.fromJson(dynamic json) {
id = json['id'];
name = json['name'];
file = json['file'];
categoryId = json['category_id'];
subcategoryId = json['subcategory_id'];
userId = json['user_id'];
status = json['status'];
createdAt = json['created_at'];
updatedAt = json['updated_at'];
}
int? id;
String? name;
String? file;
int? categoryId;
int? subcategoryId;
String? userId;
String? status;
String? createdAt;
String? updatedAt;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['id'] = id;
map['name'] = name;
map['file'] = file;
map['category_id'] = categoryId;
map['subcategory_id'] = subcategoryId;
map['user_id'] = userId;
map['status'] = status;
map['created_at'] = createdAt;
map['updated_at'] = updatedAt;
return map;
}
}
//UI
import 'package:flutter/material.dart';
import 'package:livinghopegeetzaboor/Statemanagment/Library/LibraryProvider.dart';
import 'package:livinghopegeetzaboor/constant/AppColor.dart';
import 'package:livinghopegeetzaboor/sacreen/Geet/Geetsubcategory.dart';
import 'package:livinghopegeetzaboor/services/Services.dart';
import 'package:provider/provider.dart';
class Homepage extends StatefulWidget {
#override
State<Homepage> createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Center(
child: Padding(
padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 100,
width: 100,
child: Image.asset('images/logo.png')
),
// Note: Same code is applied for the TextFormField as well
// Note: Same code is applied for the TextFormField as well
// Note: Same code is applied for the TextFormField as well
Padding(
padding: EdgeInsets.all(40),
child:Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
color: AppColor.color,
),
height: 80,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: TextField(
decoration: InputDecoration(
suffixIcon: Icon(Icons.search,
size: 40,
),
enabledBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Colors.white, width:2),
),
filled: true, //<-- SEE HERE
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
),
hintText: 'Search for any songs',
),
),
),
SizedBox(width: 3,),
// Container(
// height: 59,
// width: 59,
// color:Colors.white,
// constraints: const BoxConstraints(
//
// ),
// // child: Icon(
// // Icons.search_sharp,
// // size: 40,
// // color: AppColor.color,
// // ),
// )
],
),
),
),
),
Consumer<LibraryProvider>(
builder:(BuildContext context, value, Widget? child) {
if(value.isloading){
return CircularProgressIndicator(
color: AppColor.color,
);
}else{
return GridView.builder(
itemCount:value.library.length,
scrollDirection: Axis.vertical,
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 4.0,
mainAxisSpacing: 4.0
),
itemBuilder: (BuildContext context, int index){
var subtitle=value.library[index].name2;
return Padding(
padding: EdgeInsets.all(10),
child: Material(
color: Colors.white.withOpacity(0.0),
child: InkWell(
splashColor: Colors.orange,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color: Colors.black,
),
//color: Colors.black,
height:200,
width: 200,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.all(5),
child: Center(
child: Text(value.library[index]!.name.toString(),style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18
),),
),
),
SizedBox(height: 4,),
Padding(
padding: EdgeInsets.fromLTRB(10, 0, 0, 0),
child: Center(
child: subtitle!=null ? Text(value.library[index].name2.toString(),style: TextStyle(
color: Colors.white,
fontSize: 12
),):Text('',style: TextStyle(
color: Colors.white,
fontSize: 12
),)
),
),
],
),
),
onTap: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Geetsubcategory(id: value.library[index].id,)),
);
},
),
),
);
},
);
}
},
)
],
),
// your main content
),
),
)
);
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){
Provider.of<LibraryProvider>(context,listen: false).fetchlibary();
// Add Your Code here.
});
}
}
You need to add the api call from inside your provider function.
Like this
class SearchProvider with ChangeNotifier, DiagnosticableTreeMixin {
List<dynamic>? _items;
List<dynamic>? get items => _items;
void searchItems(String val) async {
_items = await ApiCall.search(val);
notifyListeners();
}
}
You can create a common object of provider on the page to use throughout your page like this.
SearchProvider _provider = SearchProvider();
and use it like this in your provider and consumer on the same page
ChangeNotifierProvider<SearchProvider>(
create: (_) => _provider,
child: Column(
children: [
... // other code
Consumer<SearchProvider>(
builder: (context, provider, w) {
if (provider.items != null && provider.items!.isNotEmpty) {
return SearchList(list: provider.items);
}
return const Center(child: Text("Nothing found"));
},
),
]),
),
Then setup one TextField to type & fire search query. You can setup onSubmitted callback of the Textfield to call your provider method.
TextField(
onSubmitted: (val) {
if(val.length > 0){
_provider.searchItems(val);
}
else {
_provider.getAll();
}
},
decoration: const InputDecoration(
labelText: 'Search',
suffixIcon: Icon(Icons.search),
),
Using this code will call the provider method when you press enter after typing some query in TextField. That provider method will then call the api & store the response in a List then call notifyListeners() from that method to notify all the consumers. Those consumer then try to read the list & create a listview if they find data in the list.
Here is an example of full working code:
https://dartpad.dev/?id=a5950f2da95d58e32dc9176433d9bcb3
I have also added the onload list which can be filtered from api itself & it user delete search query it will again call the full list api for data.
Hope this will help you understand things better using provider.

Search from a list of Firebase Users with TextField and StreamProvider in Flutter

I'm building a chat app with Firebase in flutter and I want to be able to search from a list of users,. I also want that when no text is typed, no user should be shown
I tried a lot of things but I never got it right. This is what I did :
search_page.dart:
class SearchPage extends StatefulWidget {
const SearchPage({Key? key}) : super(key: key);
#override
State<SearchPage> createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
TextEditingController searchController = TextEditingController();
#override
void initState() {
super.initState();
searchController.addListener(_onSearchChanged);
}
_onSearchChanged() {
print(searchController.text);
}
#override
void dispose() {
searchController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return StreamProvider<List<AppUserData>>.value(
initialData: [],
value: DatabaseService().users,
child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: dDarkGrey,
body:
Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
const SizedBox(
height: 3,
),
Stack(
alignment: Alignment.center,
children: [
Container(
height: 90,
decoration: BoxDecoration(color: dDarkGrey, boxShadow: [
BoxShadow(
color: dBlack.withOpacity(0.16),
spreadRadius: 3,
blurRadius: 3,
offset: const Offset(0, 4))
]),
),
Column(
children: [
const SizedBox(
height: 20,
),
Row(
children: [
IconButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => const HomePage()));
},
icon: const Icon(SocketIconv2.ic_back),
color: dLightGrey,
),
SizedBox(
width: MediaQuery.of(context).size.width - 60,
child: TextField(
enabled: true,
controller: searchController,
style: const TextStyle(color: dLightGrey),
decoration: const InputDecoration(
contentPadding:
EdgeInsets.fromLTRB(0, 0, 0, 0),
filled: true,
fillColor: dDarkGrey,
prefixIcon: Icon(
SocketIconv2.ic_search,
color: dLightGrey,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(15))),
hintStyle: TextStyle(
color: dLightGrey,
fontFamily: 'SegoeUI',
fontSize: 18,
fontWeight: FontWeight.w300),
hintText: 'Search',
))),
],
),
],
),
],
),
// search bar
SizedBox(
height: MediaQuery.of(context).size.height - 95,
width: MediaQuery.of(context).size.width,
child: SearchList(
controller: searchController,
),
)
])),
);
}
}
search_list.dart:
class SearchList extends StatelessWidget {
SearchList({Key? key, required this.controller}) : super(key: key);
final TextEditingController controller;
#override
Widget build(BuildContext context) {
final users = Provider.of<List<AppUserData>>(context);
return Scaffold(
backgroundColor: Colors.transparent,
body: ListView.separated(
itemBuilder: (context, index) {
if (controller.text.isEmpty) {
return Container();
}
if (controller.text.isNotEmpty) {
return searchAccount(context, name, username, users[index]);
}
if (users[index].name.startsWith(controller.text.toLowerCase())) {
return searchAccount(context, name, username, users[index]);
} else {
return Container();
}
},
itemCount: users.length,
separatorBuilder: (context, index) => const SizedBox(height: 20),
));
}
}
search_account.dart:
Widget searchAccount(
BuildContext context, String name, String username, AppUserData users) {
return Row(
children: [
const SizedBox(
width: 20,
),
Row(
children: [
ClipOval(
child: Image.asset(
imagePp,
scale: 9,
),
),
const SizedBox(
width: 30,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(users.name,
style: SegoeUiPreset(dLightGrey).customTileName()),
Text(users.username,
style: SegoeUiPreset(dLightGrey).customTileSubtitle())
],
)
],
),
],
);
}
user.dart:
class AppUser {
final String? uid;
AppUser({this.uid});
}
class AppUserData {
final String? uid;
final String name;
final String username;
AppUserData({this.uid, required this.name, required this.username});
}
database.dart:
class DatabaseService {
final String? uid;
DatabaseService({this.uid});
final CollectionReference chatRoomCollection =
FirebaseFirestore.instance.collection("chatrooms");
final CollectionReference userCollection =
FirebaseFirestore.instance.collection("users");
Future<void> saveUser(String name, String username) async {
return await userCollection
.doc(uid)
.set({'name': name, 'username': username});
}
AppUserData _userFromSnapshot(DocumentSnapshot snapshot) {
return AppUserData(
name: snapshot['name'],
uid: snapshot.id,
username: snapshot['username']);
}
Stream<AppUserData> get user {
return userCollection.doc(uid).snapshots().map(_userFromSnapshot);
}
List<AppUserData> _userListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map((doc) {
return _userFromSnapshot(doc);
}).toList();
}
Stream<List<AppUserData>> get users {
return userCollection.snapshots().map(_userListFromSnapshot);
}
}
Got any hints ? I'm a begginner :/
Thank you in advance :)

How to save edited todo item?

For the application, I create the functionality of editing todo item. Item has two fields: title and description. When you click on an item, it opens a modal where you can edit it. How to correctly write a function to the save button so that it saves the changes and displays them?
My Provider:
class ListModel extends ChangeNotifier {
List<EventModel> eventList = [];
void addEventToList() {
EventModel eventModel = EventModel(
title: 'Event title ${eventList.length + 1}',
detail: 'Event text ${eventList.length + 1}',
id: '${eventList.length + 1}',
);
eventList.add(eventModel);
notifyListeners();
}
EventModel? getEvent(String? id) {
return eventList.firstOrNullWhere((event) => event.id == id);
}
}
My modal window:
class EditEventBottomSheet extends StatelessWidget {
final EventModel event;
const EditEventBottomSheet({Key? key, required this.event}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
height: 300,
color: Colors.amber,
child: Center(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Change Event',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
TextFormField(
initialValue: event.title,
),
const SizedBox(height: 10),
TextFormField(
initialValue: event.detail,
),
const SizedBox(height: 10),
ElevatedButton(
child: const Text('Save Edits'),
onPressed: () {},
),
ElevatedButton(
child: const Text('Close BottomSheet'),
onPressed: () => Navigator.pop(context),
)
],
),
),
),
);
}
}
You should have an EditingControllers for each textField,
so, define the following before your build method
late final titleController = TextEditingController(text: event.title);
late final detailController = TextEditingController(text: event.detail);
and inside your build method
final provider = Provider.of<ListModel>(context);
TextFormField(
controller: titleController,
),
const SizedBox(height: 10),
TextFormField(
controller: detailController,
),
const SizedBox(height: 10),
ElevatedButton(
child: const Text('Save Edits'),
onPressed: () {
provider.editTodoItem(event.id, titleController.text, detailController.text);
},
),
And your editTodoItem should be something like:
void editTodoItem(final String id, final String title, final String details){
//Add a validation if you want, for example title & details must not be empty
if(valid){
final indexOfOld = eventList.map((e) => e.id).toList().indexOf(id);
if (indexOfOld == -1) return;
eventList.removeAt(indexOfOld);
eventList.insert(indexOfOld, EventModel(
title: title,
detail: details,
id: '$indexOfOld',
));
notifyListeners();
}
}
That is one of the ways of editing, (Replace the object)
Also Instead of removing the object and creating a new one with the new values and insert it in old's one index, you can do the following
if(valid){
final event = eventList.firstWhere((e) => e.id == id);
event.title = title;
event.detail = details;
notifyListeners();
}
but to be honest, the second way might not update your UI, It's been a long time since I used providers
Hope this answer helps.

Flutter - Text undo and redo button

hi i have been searching the internet for how to create redo and undo buttons and connect them to a flutter TextField, but so far i haven't found anything. I hope someone knows how to do this, I hope for your help.
You may have a look at undo or replay_bloc packages.
Or, you may just try to implement the feature in your own project and fine-tune it to your specific requirements.
Here is a draft implementation of such a feature.
It supports undo, redo and reset.
I used the following packages:
Flutter Hooks, as an alternative to StatefulWidgets
Hooks Riverpod, for State Management
Freezed, for immutability
Easy Debounce, to denounce the history of changes
You'll find the full source code at the end of this post. but, here are a few important highlights:
Structure of the solution:
App
A MaterialApp encapsulated inside a Riverpod ProviderScope
HomePage
A HookWidget maintaining the global state: uid of the selected quote and editing, whether or not we display the Form.
QuoteView
Very basic display of the selected Quote.
QuoteForm
This form is used to modify the selected quote. Before (re)building the form, we check if the quote was changed (this happens after a undo/reset/redo) and if so, we reset the values (and cursor position) of the fields that changed.
UndoRedoResetWidget
This Widget provides three buttons to trigger undo / reset and redo on our `pendingQuoteProvider. The undo and redo buttons also display the number of undo and redo available.
pendingQuoteProvider
This is a family StateNotifierProvider (check here for more info on family providers), it makes it easy and simple to track changes per quote. It even keeps the tracked changes even when you navigate from one quote to other quotes and back. You will also see that, inside our PendingQuoteNotifier, I debounce the changes for 500 milliseconds to decrease the number of states in the quote history.
PendingQuoteModel
This is the State Model of our pendingQuoteProvider. It's made of a List<Quote> history as well as an index for the current position in history.
Quote
Basic class for our Quotes, made of uid, text, author, and year.
Full source code
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:easy_debounce/easy_debounce.dart';
part '66288827.undo_redo.freezed.dart';
// APP
void main() {
runApp(
ProviderScope(
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Undo/Reset/Redo Demo',
home: HomePage(),
),
),
);
}
// HOMEPAGE
class HomePage extends HookWidget {
#override
Widget build(BuildContext context) {
final selected = useState(quotes.keys.first);
final editing = useState(false);
return Scaffold(
body: SingleChildScrollView(
child: Container(
padding: EdgeInsets.all(16.0),
alignment: Alignment.center,
child: Column(
children: [
Wrap(
children: quotes.keys
.map((uid) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 4.0,
vertical: 2.0,
),
child: ChoiceChip(
label: Text(uid),
selected: selected.value == uid,
onSelected: (_) => selected.value = uid,
),
))
.toList(),
),
const Divider(),
ConstrainedBox(
constraints: BoxConstraints(maxWidth: 250),
child: QuoteView(uid: selected.value),
),
const Divider(),
if (editing.value)
ConstrainedBox(
constraints: BoxConstraints(maxWidth: 250),
child: QuoteForm(uid: selected.value),
),
const SizedBox(height: 16.0),
ElevatedButton(
onPressed: () => editing.value = !editing.value,
child: Text(editing.value ? 'CLOSE' : 'EDIT'),
)
],
),
),
),
);
}
}
// VIEW
class QuoteView extends StatelessWidget {
final String uid;
const QuoteView({Key key, this.uid}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('“${quotes[uid].text}”', textAlign: TextAlign.left),
Text(quotes[uid].author, textAlign: TextAlign.right),
Text(quotes[uid].year, textAlign: TextAlign.right),
],
);
}
}
// FORM
class QuoteForm extends HookWidget {
final String uid;
const QuoteForm({Key key, this.uid}) : super(key: key);
#override
Widget build(BuildContext context) {
final quote = useProvider(
pendingQuoteProvider(uid).state.select((state) => state.current));
final quoteController = useTextEditingController();
final authorController = useTextEditingController();
final yearController = useTextEditingController();
useEffect(() {
if (quoteController.text != quote.text) {
quoteController.text = quote.text;
quoteController.selection =
TextSelection.collapsed(offset: quote.text.length);
}
if (authorController.text != quote.author) {
authorController.text = quote.author;
authorController.selection =
TextSelection.collapsed(offset: quote.author.length);
}
if (yearController.text != quote.year) {
yearController.text = quote.year;
yearController.selection =
TextSelection.collapsed(offset: quote.year.length);
}
return;
}, [quote]);
return Form(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
UndoRedoResetWidget(uid: uid),
TextFormField(
decoration: InputDecoration(
labelText: 'Quote',
),
controller: quoteController,
keyboardType: TextInputType.multiline,
maxLines: null,
onChanged: (value) =>
context.read(pendingQuoteProvider(uid)).updateText(value),
),
TextFormField(
decoration: InputDecoration(
labelText: 'Author',
),
controller: authorController,
onChanged: (value) =>
context.read(pendingQuoteProvider(uid)).updateAuthor(value),
),
TextFormField(
decoration: InputDecoration(
labelText: 'Year',
),
controller: yearController,
onChanged: (value) =>
context.read(pendingQuoteProvider(uid)).updateYear(value),
),
],
),
);
}
}
// UNDO / RESET / REDO
class UndoRedoResetWidget extends HookWidget {
final String uid;
const UndoRedoResetWidget({Key key, this.uid}) : super(key: key);
#override
Widget build(BuildContext context) {
final pendingQuote = useProvider(pendingQuoteProvider(uid).state);
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
_Button(
iconData: Icons.undo,
info: pendingQuote.hasUndo ? pendingQuote.nbUndo.toString() : '',
disabled: !pendingQuote.hasUndo,
alignment: Alignment.bottomLeft,
onPressed: () => context.read(pendingQuoteProvider(uid)).undo(),
),
_Button(
iconData: Icons.refresh,
disabled: !pendingQuote.hasUndo,
onPressed: () => context.read(pendingQuoteProvider(uid)).reset(),
),
_Button(
iconData: Icons.redo,
info: pendingQuote.hasRedo ? pendingQuote.nbRedo.toString() : '',
disabled: !pendingQuote.hasRedo,
alignment: Alignment.bottomRight,
onPressed: () => context.read(pendingQuoteProvider(uid)).redo(),
),
],
);
}
}
class _Button extends StatelessWidget {
final IconData iconData;
final String info;
final Alignment alignment;
final bool disabled;
final VoidCallback onPressed;
const _Button({
Key key,
this.iconData,
this.info = '',
this.alignment = Alignment.center,
this.disabled = false,
this.onPressed,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPressed,
child: Stack(
children: [
Container(
width: 24 + alignment.x.abs() * 6,
height: 24,
decoration: BoxDecoration(
color: Colors.black12,
border: Border.all(
color: Colors.black54, // red as border color
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(alignment.x == -1 ? 10.0 : 0.0),
topRight: Radius.circular(alignment.x == 1 ? 10.0 : 0.0),
bottomRight: Radius.circular(alignment.x == 1 ? 10.0 : 0.0),
bottomLeft: Radius.circular(alignment.x == -1 ? 10.0 : 0.0),
),
),
),
Positioned.fill(
child: Align(
alignment: Alignment(alignment.x * -.5, 0),
child: Icon(
iconData,
size: 12,
color: disabled ? Colors.black38 : Colors.lightBlue,
),
),
),
Positioned.fill(
child: Align(
alignment: Alignment(alignment.x * .4, .8),
child: Text(
info,
style: TextStyle(fontSize: 6, color: Colors.black87),
),
),
),
],
),
).showCursorOnHover(
disabled ? SystemMouseCursors.basic : SystemMouseCursors.click);
}
}
// PROVIDERS
final pendingQuoteProvider =
StateNotifierProvider.family<PendingQuoteNotifier, String>(
(ref, uid) => PendingQuoteNotifier(quotes[uid]));
class PendingQuoteNotifier extends StateNotifier<PendingQuoteModel> {
PendingQuoteNotifier(Quote initialValue)
: super(PendingQuoteModel().afterUpdate(initialValue));
void updateText(String value) {
EasyDebounce.debounce('quote_${state.current.uid}_text', kDebounceDuration,
() {
state = state.afterUpdate(state.current.copyWith(text: value));
});
}
void updateAuthor(String value) {
EasyDebounce.debounce(
'quote_${state.current.uid}_author', kDebounceDuration, () {
state = state.afterUpdate(state.current.copyWith(author: value));
});
}
void updateYear(String value) {
EasyDebounce.debounce('quote_${state.current.uid}_year', kDebounceDuration,
() {
state = state.afterUpdate(state.current.copyWith(year: value));
});
}
void undo() => state = state.afterUndo();
void reset() => state = state.afterReset();
void redo() => state = state.afterRedo();
}
// MODELS
#freezed
abstract class Quote with _$Quote {
const factory Quote({String uid, String author, String text, String year}) =
_Quote;
}
#freezed
abstract class PendingQuoteModel implements _$PendingQuoteModel {
factory PendingQuoteModel({
#Default(-1) int index,
#Default([]) List<Quote> history,
}) = _PendingModel;
const PendingQuoteModel._();
Quote get current => index >= 0 ? history[index] : null;
bool get hasUndo => index > 0;
bool get hasRedo => index < history.length - 1;
int get nbUndo => index;
int get nbRedo => history.isEmpty ? 0 : history.length - index - 1;
PendingQuoteModel afterUndo() => hasUndo ? copyWith(index: index - 1) : this;
PendingQuoteModel afterReset() => hasUndo ? copyWith(index: 0) : this;
PendingQuoteModel afterRedo() => hasRedo ? copyWith(index: index + 1) : this;
PendingQuoteModel afterUpdate(Quote newValue) => newValue != current
? copyWith(
history: [...history.sublist(0, index + 1), newValue],
index: index + 1)
: this;
}
// EXTENSIONS
extension HoverExtensions on Widget {
Widget showCursorOnHover(
[SystemMouseCursor cursor = SystemMouseCursors.click]) {
return MouseRegion(cursor: cursor, child: this);
}
}
// CONFIG
const kDebounceDuration = Duration(milliseconds: 500);
// DATA
final quotes = {
'q_5374': Quote(
uid: 'q_5374',
text: 'Always pass on what you have learned.',
author: 'Minch Yoda',
year: '3 ABY',
),
'q_9534': Quote(
uid: 'q_9534',
text: "It’s a trap!",
author: 'Admiral Ackbar',
year: "2 BBY",
),
'q_9943': Quote(
uid: 'q_9943',
text: "It’s not my fault.",
author: 'Han Solo',
year: '7 BBY',
),
};

Not able to change a value in one page with respect to the value from another page in flutter

i want to change the indexvalue (pictogramindex) of one page when we click nextbutton on another screen.I will explain briefly , I have 2 screens in my scenario the first screen contains an image and it's name , a textfield and nextbutton (i have provided a dummy data contains a list of image and it's names) the logic behind this is , when we complete the textfield box and click next button(after validate) the textfield value checks with the correctvalue which i was given in the dummy data and show it's synonym which also provided. when we click the next button we will go to another page which contains the correct answer(passed from first page) and a textfield in this the user can write about the correct answer ( validated) when click next button in this page (till this my applicationworks perfectly) i want to load the first page with it's index updated (+1) which i initialised as 0 (var pictogramindex=0). But in my case when coming back to first page the index is not updating it will automatically stores the initialised value. what i want is i want to update index on the first page when i click next button in the Second page .
my source code of first screen is shown here
class Pictogramscreen extends StatefulWidget {
final int length;
const Pictogramscreen({Key key, this.length}) : super(key: key);
#override
_PictogramscreenState createState() => _PictogramscreenState();
}
class _PictogramscreenState extends State<Pictogramscreen> {
#override
final _Key = GlobalKey<FormState>();
Color defaultcolor = Colors.blue[50];
Color trueColor = Colors.green;
Color falseColor = Colors.red;
Widget defcorrect = Text('');
var pictogramindex = 0;
TextEditingController usertitleInput = TextEditingController();
nextPictogram() {
setState(() {
pictogramindex++;
});
}
fillColor() {
setState(() {
usertitleInput.text == pictdata[pictogramindex]['pictcorrectword']
? defaultcolor = trueColor
: defaultcolor = falseColor;
});
}
correctText() {
setState(() {
usertitleInput.text == pictdata[pictogramindex]['pictcorrectword']
? defcorrect = Text(pictdata[pictogramindex]['pictsynonym'])
: defcorrect = Text(pictdata[pictogramindex]['pictcorrectword']);
});
}
reset() {
setState(() {
defaultcolor = Colors.blue[50];
defcorrect = Text('');
usertitleInput.clear();
});
}
void description(BuildContext ctx) {
Navigator.of(context).pushNamed('/user-description', arguments: {
'id': pictdata[pictogramindex]['pictid'],
'word': pictdata[pictogramindex]['pictcorrectword']
});
}
Widget build(BuildContext context) {
int length = pictdata.length;
return Scaffold(
body: pictogramindex < pictdata.length
? ListView(
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 20),
padding: EdgeInsets.all(15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Card(
margin: EdgeInsets.only(top: 20),
child: Image.network(
pictdata[pictogramindex]['pictimg']),
),
SizedBox(
height: 10,
),
Text(
pictdata[pictogramindex]['pictword'],
style: TextStyle(
fontSize: 25,
),
),
SizedBox(
height: 10,
),
//Card(
//color: Colors.blue,
// child: TextField(
// decoration: InputDecoration.collapsed(
// hintText: 'type here'),
//textAlign: TextAlign.center,
// onSubmitted: (value) {
// usertitleInput = value;
// print(usertitleInput);
// },
// ),
//),
Form(
key: _Key,
child: TextFormField(
controller: usertitleInput,
validator: (usertitleInput) {
if (usertitleInput.isEmpty) {
return 'Answer cannot be empty';
} else {
return null;
}
},
textAlign: TextAlign.center,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide:
BorderSide(color: Colors.blueAccent),
borderRadius: BorderRadius.all(
Radius.circular(15),
)),
labelText: 'Type your Answer',
filled: true,
fillColor: defaultcolor,
),
onFieldSubmitted: (value) {
usertitleInput.text = value;
fillColor();
correctText();
print(usertitleInput.text);
}),
),
SizedBox(
height: 10,
),
defcorrect,
SizedBox(
height: 10,
),
RaisedButton(
onPressed: () {
if (_Key.currentState.validate()) {
description(context);
// nextPictogram();
reset();
}
//
//if (_Key.currentState.validate() == correctText()) {
// nextPictogram;
// }
},
child: Text('Next'),
)
],
),
),
],
)
: Center(
child: Text('completed'),
));
}
}
my source code of the second screen is show here
class Userinputscreen extends StatefulWidget {
final String id;
final String word;
const Userinputscreen({Key key, this.id, this.word}) : super(key: key);
#override
_UserinputscreenState createState() => _UserinputscreenState();
}
class _UserinputscreenState extends State<Userinputscreen> {
final _Keey = GlobalKey<FormState>();
TextEditingController userdescription = TextEditingController();
var pictogramindex;
void nextpict(BuildContext context) {
Navigator.of(context).pushNamed('/main-screen');
}
// void nextpict(BuildContext context, int index) {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (ctx) => Pictogramscreen(
// index: i = 0,
// )));
// }
#override
Widget build(BuildContext context) {
final routeArgs =
ModalRoute.of(context).settings.arguments as Map<String, String>;
final correctWord = routeArgs['word'];
return MaterialApp(
home: Scaffold(
body: ListView(children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 50),
child: Center(
child: Container(
padding: EdgeInsets.all(20),
margin: EdgeInsets.only(top: 100),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
correctWord,
style: TextStyle(fontSize: 26),
),
SizedBox(
height: 10,
),
Form(
key: _Keey,
child: TextFormField(
controller: userdescription,
validator: (userdescription) {
if (userdescription.isEmpty) {
return 'Answer cannot be empty';
} else {
return null;
}
},
textAlign: TextAlign.center,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blueAccent),
borderRadius: BorderRadius.all(
Radius.circular(15),
)),
labelText: 'Type your Answer',
filled: true,
),
onFieldSubmitted: (value) {
userdescription.text = value;
print(userdescription.text);
}),
),
SizedBox(
height: 10,
),
RaisedButton(
onPressed: () {
if (_Keey.currentState.validate()) {
nextpict(context);
}
},
child: Text('Next'),
)
],
),
),
),
),
])),
);
}
}
If I get it right, you basically want to tell the initial page that it's state is updated(the index) elsewhere. You basically need to make your app "reactive".
As is said in Google Developers Tutorial:
One of the advantages of Flutter is that it uses reactive views, which you can take to the next level by also applying reactive principles to your app’s data model.
Use some sort of state management. You need to choose from and use either Bloc, InheritedWidget and InheritedModel, Provider(ScopedModel), or the like.
Check this article on flutter about state management, or this for a complete list of approaches