my provider is not found, when I use selector - flutter

I run the below code using consumer and provider and it worked smoothly but when I add selector it geive me an error,
the error:
Error: Could not find the correct Provider above this Selector<SignInUpProvider, int> Widget
This happens because you used a BuildContext that does not include the provider
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:provider/provider.dart';
import 'providers/sign_in_up_provider.dart';
import 'utilities/myColors.dart' ;
class SigInUpPage extends StatefulWidget {
const SigInUpPage({Key? key}) : super(key: key);
#override
_SigInUpPageState createState() => _SigInUpPageState();
}
class _SigInUpPageState extends State<SigInUpPage> {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(create: (context)=> SignInUpProvider(),
child: const SignInUpScreen(),);
}
}
class SignInUpScreen extends StatefulWidget {
const SignInUpScreen({Key? key}) : super(key: key);
#override
_SignInUpScreenState createState() => _SignInUpScreenState();
}
class _SignInUpScreenState extends State<SignInUpScreen> {
// determine the sign in or sign up screen!!
// 0 = sign up , 1 = sign in
// sign up contents
// final GlobalKey<FormState> _keySignUp = GlobalKey<FormState>();
final TextEditingController _nameSignUp = TextEditingController();
final TextEditingController _passSignup = TextEditingController();
final TextEditingController _emailSignUp = TextEditingController();
// sign in contents
final GlobalKey<FormState> _keySignIn = GlobalKey<FormState>();
final TextEditingController _emailSignIn = TextEditingController();
final TextEditingController _passSifnIn = TextEditingController();
#override
void dispose() {
_nameSignUp.dispose();
_passSifnIn.dispose();
_passSignup.dispose();
_emailSignIn.dispose();
_emailSignUp.dispose();
// TODO: implement dispose
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.white,
child: Stack(
children: [
// dummy design
firstScreen(context),
// dummy design
secondScreen(context),
// sign in screen
thirdScreen(context),
],
),
),
);
}
// first background in stack
Widget firstScreen(BuildContext context){
return
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color:MyColors.blueColor(),
);
}
// second background in stack
Widget secondScreen(BuildContext context){
return Align(
alignment: Alignment.bottomRight,
child: Container(
decoration: BoxDecoration(
color: MyColors.purple(),
borderRadius: const BorderRadius.only(bottomLeft:Radius.circular(1000),
topLeft: Radius.circular(1000)
)
),
width: MediaQuery.of(context).size.width * 0.5,
height: MediaQuery.of(context).size.height,
),
);
}
// sign in up screen // the white box
Widget thirdScreen(BuildContext context){
return Align(alignment: Alignment.bottomCenter,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
margin: EdgeInsets.only(bottom: MediaQuery.of(context).size.height*0.1),
width: MediaQuery.of(context).size.width*0.7,
height: MediaQuery.of(context).size.height*0.7,
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(50))),
child: Selector<SignInUpProvider, int>(
selector: (context, userState) => userState.getUserState,
builder: (context, state, widget){
return Form(
key: _keySignIn,
child: (state == 0 )?
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children:
[
logo(context),
nameBox(context),
emailBox(context),
passText(context),
signButton(context),
// this to change between sign in and sign up
changeState(context),
],
):
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children:
[
logo(context),
emailBox(context),
passText(context),
signButton(context),
// this to change between sign in and sign up
changeState(context),
],
),
);
}
),
),
),
);
}
// the logo inside the white box
Widget logo(context) {
return Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(50))
),
width: MediaQuery.of(context).size.width*0.7,
height: MediaQuery.of(context).size.height*0.1,
child: const Align(
alignment: Alignment.center,
child: Text("mekyajat",style: TextStyle(
fontStyle: FontStyle.italic,
fontSize: 16,
color: Color.fromRGBO(249, 229, 230, 1),
),)),
);
}
// name box
Widget nameBox(context){
return Container(
width: MediaQuery.of(context).size.width*0.55,
height: MediaQuery.of(context).size.height*0.1,
decoration: const BoxDecoration(
color: Colors.white70,
),
child: TextFormField(
controller:_nameSignUp ,
decoration: const InputDecoration(
icon: Icon(Icons.person),
hintText: "name",
hintStyle: TextStyle(
color: Colors.black12,
fontSize: 14
),
),
textAlign: TextAlign.center,
validator: (value){
if( value.toString().length < 8 ){
Fluttertoast.showToast(msg: "please insert your name \n more than 8 charecters",
gravity: ToastGravity.CENTER,
toastLength: Toast.LENGTH_SHORT,
backgroundColor: Colors.redAccent,
textColor: Colors.white,
fontSize: 16
);
} return null ;
},
),
);
}
Widget emailBox(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width*0.55,
height: MediaQuery.of(context).size.height*0.1,
decoration: const BoxDecoration(
color: Colors.white70,
),
child: TextFormField(
controller:(context.read<SignInUpProvider>().getUserState
== 0) ? _emailSignUp : _emailSignIn ,
decoration: const InputDecoration(
icon: Icon(Icons.email_outlined),
hintText: "E-mail #",
hintStyle: TextStyle(
color: Colors.black12,
fontSize: 14
),
),
textAlign: TextAlign.center,
validator: (value){
if( value.toString().length < 10 ){
Fluttertoast.showToast(msg: "please insert your email \n ",
gravity: ToastGravity.CENTER,
toastLength: Toast.LENGTH_SHORT,
backgroundColor: Colors.redAccent,
textColor: Colors.white,
fontSize: 16
);
}
else if (!containSymbol(value.toString(), "#")){
return "please insert # symbol";
}
return null ;
},
),
);
}
Widget passText(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width*0.55,
height: MediaQuery.of(context).size.height*0.1,
decoration: const BoxDecoration(
color: Colors.white70,
),
child: TextFormField(
controller:(context.read<SignInUpProvider>().getUserState ==0)? _passSignup :_passSifnIn ,
decoration: const InputDecoration(
icon: Icon(Icons.password_sharp),
hintText: "password",
hintStyle: TextStyle(
color: Colors.black12,
fontSize: 14
),
),
textAlign: TextAlign.center,
validator: (value){
if( value.toString().length < 8 ){
Fluttertoast.showToast(msg: "please insert your password \n more than 8 charecters",
gravity: ToastGravity.CENTER,
toastLength: Toast.LENGTH_SHORT,
backgroundColor: Colors.redAccent,
textColor: Colors.white,
fontSize: 16
);
} return null ;
},
),
);
}
Widget signButton(BuildContext context) {
return Selector<SignInUpProvider, int>(
selector: (context,userState)=> userState.getUserState,
builder: (context, currentState, widget){
return InkWell(
child: Container(
alignment: Alignment.center,
width: MediaQuery.of(context).size.width*0.40,
height: MediaQuery.of(context).size.height*0.06,
child: (currentState == 0 )?
const Text("SIGN UP", textAlign: TextAlign.center,style: TextStyle(color: Colors.white),):
const Text("SIGN IN",textAlign: TextAlign.center, style: TextStyle(color: Colors.white),),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(25)),
gradient: LinearGradient(colors: [
MyColors.blueColor(),
MyColors.purple(),
]),
),
),
onTap: (){},
);
},
);
}
Widget changeState(BuildContext context) {
return Consumer<SignInUpProvider>(
builder: (context, userSt , widget){
return InkWell(
child: SizedBox(
width: MediaQuery.of(context).size.width*0.55,
height: MediaQuery.of(context).size.height*0.05,
child:(userSt.userState == 0)?
const Text("sign in",
textAlign: TextAlign.right,
style: TextStyle(color:Color.fromRGBO(205, 221, 232, 1),fontSize: 12,
),) :
const Text("sign up",
textAlign: TextAlign.right,
style: TextStyle(
color:Color.fromRGBO(205, 221, 232, 1),fontSize: 12,
),)
),
onTap: (){
userSt.changeUserState(userSt.userState);
// SignInUpProvider().changeUserState(userState);
// signProvider.changeUserState(signProvider.userState);
},
);
},
);
}
}
// provide this function with one char and one string
// this run to see if the string contains char or not
bool containSymbol(String fullString , String char){
bool _result = false ;
List<String> _chars = [];
int _stringLingth = fullString.length;
int _keyLoop = 0 ;
while (_keyLoop < _stringLingth){
_chars.add(fullString[_keyLoop]);
_keyLoop++;
}
for(int x = 0 ; x < _chars.length; x++){
if(_chars[x] == char){
_result = true;
break;
}
}
return _result;
}

1 - move up your ChangeNotifierProvider step above in the widget tree for example in the routes you can wrap the SigInUpPage like this :
ChangeNotifierProvider<SignInUpProvider>(
create: (_) => SignInUpProvider(),
child: SigInUpPage(),
),
2 - don't pass the context as a parameter to the other screens,use a builder in each screen like this for the second screen:
class secondScreen extends StatelessWidget {
const secondScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.bottomCenter,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
margin: EdgeInsets.only(bottom: MediaQuery.of(context).size.height*0.1),
width: MediaQuery.of(context).size.width*0.7,
height: MediaQuery.of(context).size.height*0.7,
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(50))),
child: Selector<SignInUpProvider, int>(
selector: (context, userState) => userState.getUserState,
builder: (context, state, widget){
return Form(
key: _keySignIn,
child: (state == 0 )?
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children:
[
logo(context),
nameBox(context),
emailBox(context),
passText(context),
signButton(context),
// this to change between sign in and sign up
changeState(context),
],
):
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children:
[
logo(context),
emailBox(context),
passText(context),
signButton(context),
// this to change between sign in and sign up
changeState(context),
],
),
);
}
),
),
),
);
}
}
this should work

Related

Why does futurebuilder keep fetching data everytime TextFormField is pressed?

I have a statelsess widget built like this where a futurebuilder fetches data and then returns a StatefullTextFormFieldWidget:
Statless Widget{
build:
Futurebuilder(snapshot, context){
future: GetDocumentFromFirebase(id);
if(snapshot.hasData){
return StatefullTextFormFieldWidget(snapshot);
}
}
}
The StatefullTextFormfieldWidget contains a appbar and scaffold with with 2 TextFormFields and workes as it should (when it was offline).
The bug occurs when any of my forms gets onFocusScope in the StatefullTextFormFieldWidget. Then the future starts to refetch data from firebase. It does NOT trigger the rebuild function so my app actually works fine since the state remains, but the main problem is that the app unnecessarily starts fetching data from firestore everytime the user clicks on a TextFormField. I only want to fetch data once when the user enter the screen and then stick with that snapshot as long as the user remains on the screen.
I really cant understand how the futurebuilder can continue fetch data without being rebuilt.
FuturebuilderWidget:
import 'package:flutter/material.dart';
import 'package:MyApp/manage_orders/handle/widgets/form_has_handling.dart';
import 'package:MyApp/services/firestore.dart';
import 'package:MyApp/services/models.dart';
class HandleOrderScreen extends StatefulWidget {
static const routeName = '/handle-order-screen';
#override
State<HandleOrderScreen> createState() => _HandleOrderScreenState();
}
class _HandleOrderScreenState extends State<HandleOrderScreen> {
#override
Widget build(BuildContext context) {
final String id = ModalRoute.of(context)!.settings.arguments as String;
return FutureBuilder<Map>(
future: FirestoreService().getOrderPriceUserBoat(id),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
appBar: AppBar(
title: Text(''),
),
body: Center(
child: Center(child: CircularProgressIndicator()),
),
);
} else if (snapshot.hasError) {
return Scaffold(
appBar: AppBar(
title: Text('Error'),
),
body: Center(
child: Text(snapshot.error.toString()),
),
);
} else if (snapshot.hasData) {
var snapshotData = snapshot.data;
Order order = snapshotData!['order'];
return TextFormFieldWidget(order: order);
} else {
return Scaffold(
appBar: AppBar(
title: Text('Error'),
),
body: Center(
child: Text('Something went wrong'),
),
);
}
});
}
}
TextFormFieldWidget:
import 'package:flutter/material.dart';
import 'package:MyApp/services/models.dart';
class TextFormFieldWidgetextends StatefulWidget {
const TextFormFieldWidget({
Key? key,
required this.order,
}) : super(key: key);
final Order order;
#override
State<FormHasHandlingWidget> createState() => _TextFormFieldWidgetState();
}
class _TextFormFieldWidgetState extends State<TextFormFieldWidget> {
final commentController = TextEditingController();
final priceController = TextEditingController();
static final formkey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text('Handle'),
),
body: SingleChildScrollView(
child: Column(
children: [
Container(
padding: EdgeInsets.all(5),
height: size.height - 120,
child: Form(
key: formkey,
child: Column(
children: [
Container(
padding: EdgeInsets.all(2),
width: double.infinity,
height: 120,
child: Card(
color: Color.fromARGB(255, 241, 235, 235),
child: Container(
child: RichText(
text: TextSpan(
style:
TextStyle(color: Colors.black, fontSize: 20),
children: <TextSpan>[
TextSpan(
text: 'Test Name\n',
),
TextSpan(
text: 'Order.nr:',
style: TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(
text: '${widget.order.orderNumber} \n',
),
TextSpan(
text: 'Car: ',
style: TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(
text: widget.order.carModel + '\n',
),
],
),
),
),
),
),
Container(
padding: EdgeInsets.all(2),
child: Align(
alignment: Alignment.topLeft,
child: Text(
" Type:",
style: TextStyle(color: Colors.grey),
),
),
),
SizedBox(
height: 10,
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.black, width: 2),
),
child: Container(
padding: EdgeInsets.all(5),
child: TextFormField(
controller: priceController,
decoration: InputDecoration(labelText: 'Price: '),
validator: (value) {
if (value != null) {
if (value.length < 1) {
return 'Wright the price';
} else {
return null;
}
}
},
keyboardType: TextInputType.number,
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 20),
),
),
),
SizedBox(
height: 10,
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.black, width: 2),
),
child: Container(
padding: EdgeInsets.all(5),
child: TextFormField(
controller: commentController,
decoration:
InputDecoration(labelText: 'Description:'),
keyboardType: TextInputType.multiline,
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 20),
minLines: 2,
maxLines: 5,
validator: (value) {
if (value != null) {
if (value.length < 10) {
return 'Give a short description';
} else {
return null;
}
}
},
),
),
),
Container(
padding: EdgeInsets.all(5),
child: ElevatedButton(
child: Text('Send in'),
onPressed: () {},
style: ElevatedButton.styleFrom(
fixedSize: Size(size.width, 50),
primary: Color.fromARGB(255, 223, 208, 0)),
),
),
Spacer(),
Container(
padding: EdgeInsets.all(5),
child: ElevatedButton(
child: Text('Finish order'),
onPressed: () {},
style: ElevatedButton.styleFrom(
fixedSize: Size(size.width, 50),
primary: Color.fromARGB(255, 255, 17, 0)),
),
),
],
),
),
),
SizedBox(
height: 200,
),
],
),
),
);
}
}
My guess is that your GetDocumentFromFirebase function does a new get() call to Firestore each time it runs, which happens every time the widget is rendered.
If you want to prevent refetching the document every time, put the code in a stateful widget, and keep the Future<DocumentSnapshot> in a state variable that you only initialize once.
Also see Randal's excellent explanation on Fixing a common FutureBuilder and StreamBuilder problem

Flutter -Widget Text, not updating when value change even with SetState

I have created a container. His child is a text.
When I tap on the container/text, it display a modal and a Picker.
Then, the user can select a value. Press the confirm button and my text widget should change to display the value selected by the user.
But in my case, it is not updating the value of the text widget. I have used that in the past and it was working very well. But here it is not and I do not see why. I am on this since 8:00. A little help would be appreciated. Many thanks.
int valuePickerUnitSelected =0;
String unitCount = '';
int unitCountInt;
String goal = "";
List <String> unitForHabits = ['Count', 'Minute(s)','Hour(s)','Gramme(s)', 'Pound(s)'];
class AddingHabitDetails extends StatefulWidget {
const AddingHabitDetails({Key key}) : super(key: key);
#override
_AddingHabitDetailsState createState() => _AddingHabitDetailsState();
}
class _AddingHabitDetailsState extends State<AddingHabitDetails> {
BuildContext get ctx => null;
#override
Widget build(BuildContext context) {
return Scaffold(
//drawer: new MyMenu(), //TODO a remettre
appBar: new AppBar(
title: new Text('Habits'),
),
body: Column(
children: [
titleZone('Name'),
textFieldHabits('Habit name', context),
titleZone('Goals'),
FlatButton(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (unitCount.length < 2 )...[
Container(
width: 65,
decoration: BoxDecoration(
border: Border.all(color: Colors.blue,
),
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(20))),
child:
Center(child: Text(
'Time', style: TextStyle(color: Colors.black,))))
]
else
...[
Container(
width: 65,
decoration: BoxDecoration(
border: Border.all(color: Colors.blue,),
color: Colors.blue,
borderRadius: BorderRadius.all(
Radius.circular(20))),
child: Center(
child: Text(
unitCount, style: TextStyle(color: Colors.black,))))
],
],
),
onPressed: () {
setState(() {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return ShowPickerUnite(unitForHabits);
});
},
);
},
),
//ChipGoalV3(ctx),// ChipGoal(),
textFieldHabits('Goals', context),
titleZone('Frequency'),
textFieldHabits('Frequency', context),
titleZone('Time Range'),
titleZone('Reminder'),
titleZone('Habits Term'),
],
),
);
}
}
//########################################################################
class ShowPickerUnite extends StatefulWidget {
List<String> myListUnit;
ShowPickerUnite(this.myListUnit, {Key key}) : super(key: key);
#override
_ShowPickerUniteState createState() => _ShowPickerUniteState(
myListUnit);
}
class _ShowPickerUniteState extends State<ShowPickerUnite> {
List <String> myListUnit;
_ShowPickerUniteState(this.myListUnit);
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
decoration: BoxDecoration(
color: Color(0xffffffff),
border: Border(
bottom: BorderSide(
color: Color(0xffffffff),
width: 0.0,
),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CupertinoButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 5.0,
),
),
DefaultTextStyle(
style: TextStyle(
fontSize: 16.0,
color: Colors.black,
fontWeight: FontWeight.bold),
child: Text('Select what you want'),
),
// Text('Energy Needed', style: TextStyle(fontSize: 12.0, color: Colors.black),
// ),
CupertinoButton(
child: Text('Confirm'),
onPressed: () {
setState(() {
unitCount = unitForHabits[valuePickerUnitSelected];
print(unitCount);
});
Navigator.of(context).pop();
},
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 5.0,
),
),
],
),
),
Container(
//width: 360,
height: 200,
decoration:BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(15.0)),
),
child: CupertinoPicker(
backgroundColor: Colors.white ,
useMagnifier: true,
magnification: 1.3,
scrollController: FixedExtentScrollController(initialItem: 0),
itemExtent: 25,
children: [
for (String name in myListUnit)
Center(
child:Text(name)),
],
onSelectedItemChanged: (value) {
setState(() {
valuePickerUnitSelected = value;
// taskEnergy = myListEnergy[valuePickerEnergySelected];
// taskNewValue ['task_Energy'] = taskEnergy;
});
}))
]);
}
}
Widget inputNameHabit (){
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text ('Name Habits',style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold),),
);
}
Widget titleZone (String _titleName){
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text ( _titleName,
style: TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold),),
],
),
);
}
Widget textFieldHabits (String item,context){
return TextField(
decoration: InputDecoration(
hintText: item,
filled: true,
fillColor: Colors.grey[300],
border: OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.circular(50)
),
),
onTap: (){
Navigator.push(context,
MaterialPageRoute(
builder: (context) => HabitGoalUnitSelection(), //TODO MODIFIER route selon source
),
);
},);
}
Wait for the dialog to finish and then call setState to update the UI.
Modify this way
onPressed: () async {
await showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return ShowPickerUnite(unitForHabits);
});
setState(() {});

There is a problem that is rebuild when I click TextField() in Flutter

My code includes FutureBuilder(), which get data from Firestore, and its child widgets include GridView.builder and TextField widgets etc.
When I click on a TexField(focus), the codes in FutureBuilder are rebuild.
The following is the test code for this.
Can you tell me the cause and solution of this problem?
class TestRoom extends StatefulWidget {
TestRoom({Key? key}) : super(key: key);
#override
State<TestRoom> createState() => _TestRoomState();
}
class _TestRoomState extends State<TestRoom> {
List<RoomModel> _roomModels = [];
TextEditingController _textEditingController = TextEditingController();
bool _isTablet = false;
#override
void dispose() {
_textEditingController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
if (size.width >= 800) {
_isTablet = true;
} else {
_isTablet = false;
}
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"Test",
style: TextStyle(
fontSize: 35,
fontWeight: FontWeight.bold,
),
),
SizedBox(
height: 20,
),
FutureBuilder(
future: _getAllRoom(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
return Expanded(
child: TestList1(
isTablet: _isTablet,
roomModels: _roomModels,
isListStyle1: true,
));
},
),
],
),
),
);
}
// get user's models from firestore
Future _getAllRoom() async {
_roomModels.clear();
_roomModels.addAll(await RoomService().getAllRoomModel("userName"));
}
}
//
//
class TestList1 extends StatefulWidget {
final isTablet;
final List<RoomModel> roomModels;
final bool isListStyle1;
TestList1({
Key? key,
required this.isTablet,
required this.roomModels,
required this.isListStyle1,
}) : super(key: key);
#override
State<TestList1> createState() => _TestList1State();
}
class _TestList1State extends State<TestList1> {
TextEditingController _textEditingController = TextEditingController();
double _paddingSize = 40.0;
#override
void dispose() {
// _textEditingController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: widget.isTablet ? 6 : 3,
childAspectRatio: 1 / 1.5,
mainAxisSpacing: _paddingSize,
crossAxisSpacing: 10,
),
itemBuilder: (context, index) {
return _buildMyRooms(widget.roomModels[index], index);
},
itemCount: widget.roomModels.length,
);
}
Widget _buildMyRooms(RoomModel roomModel, int index) {
return Column(
children: [
InkWell(
onTap: () {},
child: Container(
width: 120,
height: 168,
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.blue,
width: 2.0,
),
borderRadius: const BorderRadius.all(
Radius.circular(10),
),
),
),
),
SizedBox(
height: sm_padding,
),
PopupMenuButton<int>(
color: Colors.grey[100],
itemBuilder: (context) => _fileMenuItemLust(roomModel),
onSelected: _onSeletedFileMenu,
child: Column(
children: [
Container(
width: 130,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(
child: Text(
roomModel.roomName,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: const TextStyle(
color: Colors.blue,
fontSize: 15,
),
),
),
const Icon(
Icons.arrow_drop_down_outlined,
color: Colors.blue,
),
],
),
),
],
),
)
],
);
}
// 파일 메뉴 아이템
List<PopupMenuEntry<int>> _fileMenuItemLust(RoomModel roomModel) {
_textEditingController.text = roomModel.roomName;
return [
// 파일명
PopupMenuItem(
enabled: false,
// TODO textfield
child: TextField(
controller: _textEditingController,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
maxLines: 1,
decoration: InputDecoration(
border: _textFieldBorder(),
enabledBorder: _textFieldBorder(),
disabledBorder: _textFieldBorder(),
focusedBorder: _textFieldBorder(),
focusColor: Colors.white60,
filled: true,
fillColor: Colors.grey.withOpacity(0.3),
isDense: true, // padding 조절을 위해 추가
contentPadding: EdgeInsets.all(sm_padding),
)),
),
const PopupMenuDivider(),
PopupMenuItem(
value: 0,
child: Row(
children: [
const Icon(
Icons.copy,
),
SizedBox(
width: sm_padding,
),
const Text(
"Menu Item1",
),
],
),
),
];
}
void _onSeletedFileMenu(value) {}
OutlineInputBorder _textFieldBorder() {
return OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(20)),
borderSide: BorderSide(
color: Colors.grey.withOpacity(0.3),
width: 1,
),
);
}
}
// ======================
I modified the code based on Yeasin Sheikh's answer.
The existing problem has been solved, but a new problem has arisen.
I'm going to update the Firestore using onSubmitted() of TextField and then update my list using stream builder and future builder.
This is a modified part of the code above to solve an existing problem
class _TestRoomState extends State<TestRoom> {
late final myFuture = _getAllRoom(); // new
FutureBuilder(
future: myFuture, //_getAllRoom(), // change
The problem of constantly rebuilding the text field according to the focus has disappeared, but there is a problem that getAllRoom() is called only at the first time and cannot be called afterwards, so the new room list cannot be updated.
Here future: _getAllRoom() calls the api on every state changes.
Create a state variable for future
late final myFuture = _getAllRoom();
#override
Widget build(BuildContext context) {
And use
FutureBuilder(
future:myFuture ,
You can check Fixing a common FutureBuilder and StreamBuilder problem

Access input data from dynamically created FormTextFields with validation in Flutter

I created a custom FormBuilderTextField with built in validation and am able to build them when called through LisView.Builder. My problem now is that I am unable to access the data within each generated FormBuilderTextField and pass that data to another page. I have a GlobalKey<FormState>() on the page with the ListView.Builder but cant seem to access that data. Any advice?
Custom TextField
TextFieldEntry({
required this.name,
});
final String name;
final myDecorationField = InputDecoration(
labelText: 'textFieldName',
labelStyle: TextStyle(fontSize: 20, color: Colors.pink),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(0),
bottomRight: Radius.circular(0),
),
borderSide: BorderSide(
width: 1,
color: Colors.pink,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(0),
bottomRight: Radius.circular(0),
),
borderSide: BorderSide(
color: Colors.pink,
width: 1,
),
),
);
#override
State<TextFieldEntry> createState() => _TextFieldEntryState();
}
class _TextFieldEntryState extends State<TextFieldEntry> {
Input model = Input();
var _validator;
var _keyboard;
var _myUnit;
Widget build(BuildContext context) {
var _onSaved = (value) {
model.title = value;
};
if (widget.name == 'height') {
final heightValidate = FormBuilderValidators.compose([
FormBuilderValidators.required(context),
FormBuilderValidators.numeric(context),
FormBuilderValidators.max(context, 200),
FormBuilderValidators.min(context, 30)
]);
_validator = heightValidate;
_keyboard = TextInputType.number;
_myUnit = MyUnit();
} else {
final weightValidate = FormBuilderValidators.compose([
FormBuilderValidators.required(context),
FormBuilderValidators.numeric(context),
FormBuilderValidators.max(context, 450),
FormBuilderValidators.min(context, 30)
]);
_validator = weightValidate;
_keyboard = TextInputType.number;
_myUnit = MyUnit(imperial: 'lbs', metric: 'kg');
}
return Padding(
padding: const EdgeInsets.all(2.0),
child: Column(
children: [
Container(
constraints: BoxConstraints(maxWidth: 375, maxHeight: 50),
child: Row(
children: [
Expanded(
child: FormBuilderTextField(
name: widget.name,
decoration: widget.myDecorationField
.copyWith(labelText: widget.name),
onSaved: _onSaved,
validator: _validator,
keyboardType: _keyboard,
),
),
Container(
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(color: Colors.pink, width: 2)),
child: Row(
children: [
Container(
padding: EdgeInsets.all(5),
child: Center(
child: ValueListenableBuilder<Units>(
valueListenable: _myUnit,
builder: (context, unit, _) => AutoSizeText(
_myUnit.unitType,
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w500),
)),
),
),
Builder(
builder: (context) {
if (widget.name == 'height' ||
widget.name == 'weight' ||
{
return Container(
constraints:
BoxConstraints(maxHeight: 50, maxWidth: 60),
child: TextButton(
onPressed: () {
Units unit = _myUnit.unit;
_myUnit.unit = unit == Units.unit1
? Units.unit2
: Units.unit1;
},
child: Center(
child: Icon(
Icons.loop,
size: 30,
color: Colors.white,
),
),
),
);
} else {
return Text('');
}
},
),
],
),
),
],
),
),
SizedBox(
height: 12,
)
],
),
);
}
}
ListView.Builder Page
var title;
Input({this.title});
}
class BMI extends StatefulWidget {
static const String id = 'BMI';
#override
State<BMI> createState() => _BMIState();
}
class _BMIState extends State<BMI> {
final _formKey = GlobalKey<FormState>();
List<Input> _bmiInputs = [
Input(title: 'height'),
Input(title: 'weight'),
Input(title: 'gender'),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: 'BMI',
),
drawer: MainDrawer(),
backgroundColor: Theme.of(context).colorScheme.secondary,
body: SingleChildScrollView(
child: Column(children: [
Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
constraints: BoxConstraints(maxHeight: 500, maxWidth: 375),
child: ListView.builder(
itemCount: _bmiInputs.length,
itemBuilder: (BuildContext inp, index) {
return FittedBox(
child: TextFieldEntry(
name: _bmiInputs[index].title,
),
);
},
),
),
ElevatedButton(
child: Text('Calculate'),
onPressed: () {
_formKey.currentState?.save();
if (_formKey.currentState!.validate()) {}
print('Calculate');
// need to access Data here to pass to the calculator
// BMICalc bmiCalc;
// bmiCalc = BMICalc(
// height: ______,
// weight: ______,
// gender: _______,
// );
Navigator.push(context,
MaterialPageRoute(builder: (context) => ResultsScreen(bmi: bmiCalc.calculateBMI(),)));
})
],
),
),
])));
}
}
you use the onSaved as a parameter, you can get Data back to the first Widget
in the FormBuilderTextField it will be at the onSaved
ListView.Builder
return FittedBox(
child: TextFieldEntry(
name: _bmiInputs[index].title,
onSaved: (value) {
_bmiInputs[index].title = value;
}),
);
TextFieldEntry
typedef OnSaved(value);
TextFieldEntry({
required this.name, required this.onSaved
});
final String name;
final OnSaved onSaved;
...
...
FormBuilderTextField(
name: widget.name,
decoration: widget.myDecorationField
.copyWith(labelText: widget.name),
onSaved: widget.onSaved,
validator: _validator,
keyboardType: _keyboard,
),

Latest document not listed in firestore

I am currently doing a chat app using flutter. I have 2 users and when one users start to chat with other user ,a document will be added in the "Chats" collection. If "user 1" starts a chat, a document of unique ID will be added in the collection. But the problem is, this document is not listed when I use .get() method...Please check what is the problem...
Here is my code:
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:nebula_chat_app/backend/pics.dart';
import 'package:nebula_chat_app/main.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:nebula_chat_app/backend/FireBase.dart';
class InChat extends StatefulWidget {
final String secondUserId;
final String SeconduserName;
const InChat({Key key, this.secondUserId,this.SeconduserName}) : super(key: key);
#override
_InChatState createState() => _InChatState(secondUserId,SeconduserName);
}
class _InChatState extends State {
var messages;
String docu;
List ko;
Future> offlinemessage;
final String SecondUserId;
final String SeconduserName;
String typeMessage;
ScrollController _controller2;
TextEditingController controller2;
_InChatState(this.SecondUserId,this.SeconduserName);
#override
void initState() {
controller2 = TextEditingController();
addUser();
_controller2 = ScrollController();
database.collection("Chats").get().then((value) {
var usersinChat = value.docs.map((e) => e.id).toList();
print(usersinChat);
docu = usersinChat.singleWhere((element) => ((element == auth.currentUser.uid.toString() + SecondUserId) || (element == SecondUserId + auth.currentUser.uid.toString())),orElse: ()
=> auth.currentUser.uid.toString() + SecondUserId
);
}).then((value) {
messages = database.collection("Chats").doc(docu).collection("Messages").snapshots();
print("_________________$docu");
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(icon: Icon(Icons.arrow_back_ios_sharp,color: Colors.white,), onPressed: () => Navigator.pop(context)),
title: Text(SeconduserName,style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: GFS(25, context),
color: Colors.white
),),
actions: [
IconButton(icon: Icon(Icons.search,color: Colors.white,), onPressed: null)
],
),
body: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: Theme.of(context).primaryColorLight,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
height: MediaQuery.of(context).size.height*0.75,
width: MediaQuery.of(context).size.width,
child: StreamBuilder(
stream: messages,
builder: (context, snapshot){
if(!snapshot.hasData) return Center(child: CircularProgressIndicator(),);
else
return ListView.builder(
controller: _controller2,
itemCount: snapshot.data.docs.length,
itemBuilder: (context,int index) {
if (!snapshot.hasData) return SizedBox();
else {
if (snapshot.data.docs[index]["from"] == SecondUserId){
print("Second ======> $SecondUserId");
if(_keyboardIsVisible()){
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {_controller2.animateTo(_controller2.position.maxScrollExtent,duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn ); });
}
return ReceiveContainer(
text: snapshot.data.docs[index]["message"].toString());
}
else{
return SendContainer(
text: snapshot.data.docs[index]["message"].toString(), status: "seen",);
}
}
}
);
},
)
),
Container(
height: MediaQuery.of(context).size.height*0.1,
width: MediaQuery.of(context).size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
height: MediaQuery.of(context).size.height*0.07,
alignment: Alignment.center,
width: MediaQuery.of(context).size.width*0.75,
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
boxShadow: [BoxShadow(color: Colors.black26,spreadRadius: 1.0,blurRadius: 3.0)],
borderRadius: BorderRadius.circular(40.0)
),
child: Padding(
padding: EdgeInsets.only(left:MediaQuery.of(context).size.width*0.07),
child: TextField(
controller: controller2,
style: TextStyle(
color: Colors.black,
),
decoration: InputDecoration(
focusColor: Colors.black,
hintText: 'Type here',
border: InputBorder.none,
focusedBorder: InputBorder.none,
focusedErrorBorder: InputBorder.none,
disabledBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
hintStyle: TextStyle(
color: Colors.black26,
fontSize: GFS(20, context)
)
),
onSubmitted: (text) {
setState(() {
controller2.text = text;
});
},
),
),
),
Container(
height: MediaQuery.of(context).size.height*0.07,
width: MediaQuery.of(context).size.width*0.2,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
shape: BoxShape.circle,
boxShadow: [BoxShadow(color: Colors.black26,spreadRadius: 1.0,blurRadius: 3.0)],
),
child: InkWell(
onTap: () {
database.collection("Chats").doc(docu).collection("Messages").doc(Timestamp.now().millisecondsSinceEpoch.toString()).set(
{
"from":auth.currentUser.uid.toString(),
"message": controller2.text,
"timestamp" : Timestamp.now().millisecondsSinceEpoch
});
setState(() {
controller2.text = "";
});
SchedulerBinding.instance.addPostFrameCallback((timeStamp) {_controller2.animateTo(_controller2.position.maxScrollExtent,duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn ); });
},
child: SizedBox(
width:MediaQuery.of(context).size.width*0.1,
height:MediaQuery.of(context).size.height*0.035,
child: SvgPicture.asset(sendIcon,fit: BoxFit.contain,)),
),
)
],
),
) ///Typing Container
],
),
),
),
);
}
bool _keyboardIsVisible() {
return !(MediaQuery.of(context).viewInsets.bottom == 0.0);
}
void checkCommon() {
}
}
class ReceiveContainer extends StatelessWidget {
final String text;
const ReceiveContainer({Key key, this.text}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
alignment: Alignment.centerLeft,
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height*0.1,
),
child: Padding(
padding: EdgeInsets.symmetric(vertical: MediaQuery.of(context).size.height*0.02,horizontal:MediaQuery.of(context).size.width*0.05 ),
child: Container(
constraints: BoxConstraints(
minWidth: MediaQuery.of(context).size.width*0.1,
maxWidth: MediaQuery.of(context).size.width*0.5,
minHeight: MediaQuery.of(context).size.height*0.06,
),
decoration: BoxDecoration(
color:Theme.of(context).cardColor,
borderRadius: BorderRadius.circular(20.0),
boxShadow: [BoxShadow(color: Colors.black26,spreadRadius: 1.0,blurRadius: 2.0)]
),
child: Padding(
padding: EdgeInsets.all(MediaQuery.of(context).size.width*0.4*0.1),
child: Text(text,style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: GFS(19, context),
color:Theme.of(context).textTheme.headline1.color
),),
),
),
),
);
}
}
class SendContainer extends StatelessWidget {
final String text;
final String status;
const SendContainer({Key key, this.text,this.status}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height*0.1,
),
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
height: MediaQuery.of(context).size.height*0.03,
width: MediaQuery.of(context).size.width*0.1,
child: checkStatus(status)
),
Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).size.height*0.02,bottom: MediaQuery.of(context).size.height*0.02,left:MediaQuery.of(context).size.width*0.05 ,right:MediaQuery.of(context).size.width*0.02 ),
child: Container(
constraints: BoxConstraints(
minWidth: MediaQuery.of(context).size.width*0.1,
maxWidth: MediaQuery.of(context).size.width*0.5,
minHeight: MediaQuery.of(context).size.height*0.06,
),
decoration: BoxDecoration(
color:Theme.of(context).primaryColorDark,
borderRadius: BorderRadius.circular(20.0),
boxShadow: [BoxShadow(color: Colors.black26,spreadRadius: 1.0,blurRadius: 2.0)]
),
child: Padding(
padding: EdgeInsets.all(MediaQuery.of(context).size.width*0.4*0.1),
child: Text(text,style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: GFS(19, context),
color: Colors.white
),),
),
),
),
],
),
),
);
}
checkStatus(statusss) {
{
if(statusss == 'delivered') return SvgPicture.asset(doubleCheckIcon,fit: BoxFit.contain,color: Colors.black,);
else if (statusss == 'Not delivered') return SvgPicture.asset(checkIcon,fit: BoxFit.contain,color: Colors.black,);
else if (statusss == 'seen') return SvgPicture.asset(doubleCheckIcon,fit: BoxFit.contain,color: Colors.green,);
}
}
}