Flutter Riverpod for local and global variables - flutter

i have two text editing controllers, im trying to give those text editing controllers initial values fetched from the api. I need to declare the text editing controllers globaly and give the initial value from the api, so what i need is to use data3.firstName and data3.lastName to the global variables, if i try declaring the variables and the function inside build it doesnt work, so just need to use the fetched data to global text editing controllers so i can give initial values.
late Future<Response> futureData;
#override
void initState() {
super.initState();
futureData = fetchAccountData();
}
final _formKey = GlobalKey<FormState>();
TextEditingController editedFirstName =
TextEditingController(text: 'Hello'); // needs to be data3.firsName
TextEditingController editedLastName = TextEditingController(text: 'Riverpod looks great');// needs to be data3.lastName
Future<void> putAccountData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? authorization = prefs.getString('authorization');
var url = 'https://dev.api.wurk.skyver.co/api/v1/employees/account';
Map payload = {
"firstName": editedFirstName.text,
"lastName": editedLastName.text,
};
try {
final response = await http.put(Uri.parse(url),
headers: <String, String>{
'authorization': authorization ?? basicAuth.toString(),
"Content-Type": "application/json"
},
body: jsonEncode(payload));
} catch (er) {}
}
#override
Widget build(BuildContext context) {
var height = MediaQuery.of(context).size.height;
var width = MediaQuery.of(context).size.width;
return SafeArea(
child: WillPopScope(
onWillPop: () async {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const ProfileScreen(),
),
);
return shouldPop;
},
child: KeyboardDismisser(
gestures: const [
GestureType.onTap,
GestureType.onPanUpdateDownDirection
],
child: Form(
key: _formKey,
child: Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
backgroundColor: Colors.blue,
title: const Text(
'Edit My Profile',
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const ProfileScreen(),
),
);
},
),
),
body: FutureBuilder<Response>(
future: futureData,
builder: (context, snapshot) {
if (snapshot.hasData) {
AccountData data3 = AccountData.fromJson(
json.decode(snapshot.data!.body),
);
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(
width: width,
height: height / 1.9,
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
width: 3,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Row(
children: [
const Padding(
padding: EdgeInsets.all(30),
child: Text(
"First Name:",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold),
),
),
const Spacer(),
Padding(
padding: const EdgeInsets.all(30),
child: SizedBox(
width: width / 2.5,
child: Center(
child: TextFormField(
textAlignVertical:
TextAlignVertical.center,
//initialValue: editedFirstName.text = data3.firstName,
controller: editedFirstName,
// ..selection =
// TextSelection.collapsed(
// offset: data3
// .firstName.length),
decoration: InputDecoration(
contentPadding:
const EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 10.0),
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(10),
borderSide: const BorderSide(
color: Colors.red,
width: 1),
),
),
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold,
),
// inputFormatters: [
// LengthLimitingTextInputFormatter(15)
// ],
validator: (value) {
if (value == null ||
value.isEmpty) {
return 'Name is required';
}
return null;
},
),
),
),
),
],
),
Row(
children: [
const Padding(
padding: EdgeInsets.all(30),
child: Text(
'Last Name:',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
const Spacer(),
Padding(
padding: const EdgeInsets.all(30),
child: SizedBox(
width: width / 2.5,
child: TextFormField(
//initialValue: editedLastName.text = data3.lastName,
controller: editedLastName,
// ..selection =
// TextSelection.collapsed(
// offset:
// data3.lastName.length),
decoration: InputDecoration(
contentPadding:
const EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 10.0),
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(10),
),
),
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold),
),
),
),
],
),

Looks like you want to provide an initial value to TextField, but the value is async fetched from the server.
You are on the right track to use a TextEditingController, but since you won't have the value ready at first, you should not use TextEditingController(text: 'Hello') when creating the controller.
Instead, you can create a controller without a default value, for example: final _controller = TextEditingController(). And then after you got the data, say after calling fetchAccountData() method in initState, you can assign the data to the controller using _controller.text = fetchedValue.
I see you are also using FutureBuilder. Depending on what you want to display before the "default values" are loaded, you might or might not need the FutureBuilder anymore.
Quick demo:
class QuickDemo extends StatefulWidget {
const QuickDemo({Key? key}) : super(key: key);
#override
_QuickDemoState createState() => _QuickDemoState();
}
class _QuickDemoState extends State<QuickDemo> {
final _controller = TextEditingController();
#override
void initState() {
super.initState();
_fetchData();
}
_fetchData() async {
// do your network call here
await Future.delayed(const Duration(seconds: 1));
// and then parse your response here
final data = 'Hello';
// eventually you will have data, so assign it:
_controller.text = data;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: TextField(
controller: _controller,
),
),
);
}
}

Related

Flutter - Quantity of all Products returns same value after OnClick

To keep things short:
I have multiple products, where you can increment and decrement their value(qnty) inside the cart and after Submitting, a receipt is generated based on the cart Items.
So the Problem is whenever I try to slide the submit, the qnty of the first product I added to cart is assigned to every product, Like
• Apple: 1
• Mango: 2
• Orange: 6
Above is how it should be like
• Apple: 6
• Mango: 6
• Orange: 6
This is the result I am getting, Note: The Result is from new to old
Secondary issue is that whenever I try to write any value inside the textfield and click submit, the value still doesn't get updated!
The Code consists of 2 files:
Parent File
import 'package:ambica_medico/component/result/productcart.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_email_sender/flutter_email_sender.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:whatsapp_unilink/whatsapp_unilink.dart';
import '../../component/buttons/c_button.dart';
import '../../constant.dart';
final FirebaseAuth auth = FirebaseAuth.instance;
Stream<QuerySnapshot> getData() => FirebaseFirestore.instance
.collection('Users')
.doc(auth.currentUser?.uid)
.collection('Carts')
.snapshots();
class Cart extends StatefulWidget {
const Cart({Key? key}) : super(key: key);
#override
State<Cart> createState() => _CartState();
}
class _CartState extends State<Cart> {
String _text = '';
callback(newAbc) {
pk = newAbc;
} //Step 5: Callback ready to be passed to the the Procart.
String? product;
String qnty = '';
String? mail;
String? name;
String? address;
String? dln;
String? gst;
late final _getData = getData();
DateTime now = DateTime.now();
Map<String, String> pk = {};
#override
Widget build(BuildContext context) {
return StreamBuilder<dynamic>(
stream: _getData,
builder: (context, snapshot) {
final tilesList = <Widget>[];
if (snapshot.hasData) {
snapshot.data.docs.forEach((value) {
qnty = value.data()['SIB'].toString();
pk = {value.id: qnty}; //Step4: A map which holds every product id and qnty
final productTile = Procart(
pname: value.data()['Product'],
subtitle: value.data()['MRP'],
keyo: value.id,
controller: qnty, sib: value.data()['OSIB'], tis: value.data()['TIS'], callback: callback, //Callback passed!
);
if (_text.isEmpty) {
tilesList.add(productTile);
} else {
if (value
.data()['Product']
.toUpperCase()
.contains(_text.toUpperCase())) {
tilesList.add(productTile);
}
}
// print(pk.values); //Returns 5,1
});
return SafeArea(
child: Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
leading: Padding(
padding: const EdgeInsets.only(left: 20.0, top: 10),
child: IconButton(
icon: const Icon(
Icons.arrow_back_ios_new_rounded,
color: Colors.black,
size: 20,
),
onPressed: () {
Navigator.pop(context);
},
),
),
backgroundColor: const Color(0xFFf5f3f7),
elevation: 0,
),
body: GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: Container(
decoration: kImageBackground.copyWith(),
height: MediaQuery.of(context).size.height,
child: Stack(children: [
Column(
children: [
SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(
left: 24.0, right: 24.0, top: 40),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const Text(
'The Cart',
style: TextStyle(
fontSize: 40,
fontFamily: 'ProductSans',
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
Padding(
padding:
const EdgeInsets.only(top: 50, bottom: 50),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.white,
boxShadow: const [
BoxShadow(
color: Color(0x261B1B1A),
blurRadius: 50.0,
spreadRadius: 0,
offset: Offset(0.0, 30.0),
),
],
),
height: 70,
child: Center(
child: TextField(
onChanged: (value) {
setState(() {
_text = value;
});
},
keyboardType: TextInputType.text,
decoration: kDecorS.copyWith(
hintText: 'Search Products',
),
style: const TextStyle(
fontFamily: 'ProductSans',
fontSize: 18,
fontWeight: FontWeight.w400,
color: Color(0xff0f1511),
),
),
),
),
),
],
),
),
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.55,
child: ListView(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
children: tilesList,
),
),
],
),
Positioned(
bottom: 0,
child: SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
child: Align(
alignment: Alignment.bottomCenter,
child: Container(
width: MediaQuery.of(context).size.width,
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(42.0),
topRight: Radius.circular(42.0),
),
color: Colors.white,
),
child: Padding(
padding: const EdgeInsets.only(top: 20),
child: Column(
children: [
const Padding(
padding:
EdgeInsets.only(bottom: 10.0),
child: Center(
child: Text(
'Check and then click below to',
style: TextStyle(
fontSize: 14,
fontFamily: 'ProductSans',
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
),
),
Cbutton(
text: 'Send Order',
onPressed: () async {
String message = "";
DateTime date = DateTime(
now.year, now.month, now.day);
await FirebaseFirestore.instance
.collection('Users')
.doc(auth.currentUser?.uid)
.get()
.then((value) => {
name = value.data()!['name'],
address =
value.data()!['address'],
dln = value.data()!['dln'],
gst = value.data()!['gst'],
});
await snapshot.data.docs
.forEach((value) async {
product = value.data()['Product'];
message += '- $product = ${pk.values} \n';
});
final Email email = Email(
body:
"From:- \n\nName: $name\n\nAddress: $address\n\nDrug License No:- $dln\n\nGST No:- $gst\n\nDate:- $date \n\nDear sir,\nPlease dispatch my following order earliest possible through\n $message \n\nThanks & Regards,\n$name",
subject: 'Order Detail',
recipients: ['calagency03#gmail.com'],
isHTML: false,
);
final link = WhatsAppUnilink(
phoneNumber: '+91 2313210000',
text:
"From:- \n\nName: $name\n\nAddress: $address\n\nDrug License No:- $dln\n\nGST No:- $gst\n\nDate:- $date \n\nDear sir,\nPlease dispatch my following order earliest possible through\n $message \n\nThanks & Regards,\n$name",
);
await FlutterEmailSender.send(email);
final url = Uri.parse('$link');
await launchUrl(url, mode: LaunchMode.externalApplication,);
},
icon: const Icon(
Icons.send_rounded,
color: Colors.black,
size: 20,
),
color: const Color(0xff0f1511),
),
],
),
),
),
),
),
)
]),
),
),
),
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
});
}
}
The main logic of printing the receipt for a product is:
await snapshot.data.docs
.forEach((value) async {
product = value.data()['Product'];
message += '- $product = ${pk.values} \n';
});
Child File
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
class Procart extends StatefulWidget {
final String pname;
final String subtitle;
final String keyo; // Step1: Product id
final String sib;
final String tis;
final String controller; // Step2: Initial Value/ Qnty
final Function(Map) callback; // Step3: Callback to parents widget in which it passes updated qnty
const Procart(
{Key? key,
required this.pname,
required this.subtitle,
required this.keyo, required this.controller, required this.sib, required this.tis, required this.callback})
: super(key: key);
#override
State<Procart> createState() => _ProcartState();
}
class _ProcartState extends State<Procart> {
final FirebaseAuth auth = FirebaseAuth.instance;
late TextEditingController controller = TextEditingController(text: widget.controller);
Map<String, String> lk = {};
sub() {
setState(() {
controller.text =
(int.parse(controller.text) - 1).toString();
});
}
add() {
setState(() {
controller.text =
(int.parse(controller.text) + 1).toString();
});
}
// #override
// void didUpdateWidget(covariant Procart oldWidget) {
// // TODO: implement didUpdateWidget
// super.didUpdateWidget(oldWidget);
//
//
// }
#override
Widget build(BuildContext context) {
lk = { widget.keyo : controller.text }; // This map is used to store updated value
widget.callback(lk); // send updated value back to parent class but the value still remains 2,2,2 instead of 1,7,2
print(lk.values);
return GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Slidable(
key: Key(widget.keyo),
endActionPane: ActionPane(motion: const ScrollMotion(), children: [
SlidableAction(
// An action can be bigger than the others.
onPressed: (value) {
FirebaseFirestore.instance
.collection('Users')
.doc(auth.currentUser?.uid)
.collection('Carts')
.doc(widget.keyo)
.delete();
},
backgroundColor: const Color(0xFFD16464),
foregroundColor: Colors.white,
icon: Icons.clear_rounded,
),
]),
child: Padding(
padding: const EdgeInsets.only(left: 28.0, right: 28.0),
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10)
),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.4,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.pname,
style: const TextStyle(
fontFamily: 'Satoshi',
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color(0xff0f1511),
),
),
Text(
'${widget.subtitle} | ${widget.sib} x ${widget.tis}',
style: const TextStyle(
fontFamily: 'Satoshi',
fontSize: 12,
fontWeight: FontWeight.normal,
color: Color(0xff0f1511),
),
),
],
),
),
Row(
children: [
CircleAvatar(
radius: 16,
backgroundColor: const Color(0xff1b1b1b),
child: IconButton(
iconSize: 13,
icon: const Icon(
Icons.remove,
color: Colors.white,
),
onPressed: () {
if (int.parse(controller.text) >
int.parse(widget.sib)) {
sub();
}
},
),
),
SizedBox(
width: 80,
child: TextFormField(
textAlign: TextAlign.center,
decoration: const InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.only(left: 6, right: 6),
),
controller: controller,
onEditingComplete: () {
controller;
},
keyboardType: TextInputType.number,
style: const TextStyle(
fontFamily: 'Satoshi',
fontSize: 16.0,
fontWeight: FontWeight.bold,
color: Color(0xff0f1511),
),
),
),
CircleAvatar(
radius: 16,
backgroundColor: const Color(0x33bababa),
child: IconButton(
iconSize: 13,
icon: const Icon(
Icons.add,
color: Color(0xff1b1b1b),
),
onPressed: () {
add();
},
),
),
],
),
]),
),
),
))),
);
}
}
For more info I have attached Screenshots below:
A Huge Thank you to anyone who helps me in solving this!
Well, I solved this issue by following the steps below:

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

Why is Flutter BloC UI not updating after app restart despite state change in observer?

I concede that there are many questions similar to mine, but I have not found a satisfactory answer from those questions. So I decided to make my own question specifying my problem. I have 3 BloCs in my program each with different purposes. They all share similar problems, as such I will ask on one of those BloCs with the hope that one solution will fix all of the BloCs.
The problem is this, if I just started the application and have logged in, the BloC will update the UI. If I have logged in, exited the app, and restarted it, the Bloc will not update the UI. The Bloc in question is called DetailpersonilBloc with 1 event called Detail and 2 states called DetailpersonilInitial and Loaded. At the event of Detail, the state Loaded should be emitted.
I called Detail at LoginPage and at GajiPage at initState. This works when I just opened the app, but does not work when I restart the app. I also have equatable thinking that it will help me but apparently it changes nothing.
Note: The "..." at the GajiPage is just some code that I believe is not necessary for reproduction.
DetailpersonilBloc
part 'detailpersonil_event.dart';
part 'detailpersonil_state.dart';
class DetailpersonilBloc
extends Bloc<DetailpersonilEvent, DetailpersonilState> {
DetailpersonilBloc() : super(const DetailpersonilInitial()) {
on<Detail>((event, emit) async {
SharedPreferences pref = await SharedPreferences.getInstance();
String name = pref.getString('nama');
String nrp = pref.getString('NRP');
String pangkat = pref.getString('pangkat');
String jabatan = pref.getString('jabatan');
String satker = pref.getString('satker');
String polda = pref.getString('polda');
String npwp = pref.getString('NPWP');
String rekening = pref.getString('rekening');
String bank = pref.getString('bank');
emit(Loaded(
name,
nrp,
pangkat,
jabatan,
satker,
polda,
npwp,
rekening,
bank,
));
});
}
}
DetailpersonilEvent
part of 'detailpersonil_bloc.dart';
#immutable
abstract class DetailpersonilEvent extends Equatable {}
class Detail extends DetailpersonilEvent {
#override
List<Object> get props => [];
}
DetailpersonilState
part of 'detailpersonil_bloc.dart';
#immutable
abstract class DetailpersonilState extends Equatable {
final String nama;
final String nrp;
final String pangkat;
final String jabatan;
final String satker;
final String polda;
final String npwp;
final String rekening;
final String bank;
const DetailpersonilState(
{this.nama,
this.nrp,
this.pangkat,
this.jabatan,
this.satker,
this.polda,
this.npwp,
this.rekening,
this.bank});
}
class DetailpersonilInitial extends DetailpersonilState {
const DetailpersonilInitial()
: super(
nama: 'Nama',
nrp: 'NRP',
pangkat: 'Pangkat',
jabatan: 'Jabatan',
satker: 'Satker',
polda: 'Polda',
npwp: 'NPWP',
rekening: 'No Rekening',
bank: 'Nama Bank',
);
#override
List<Object> get props =>
[nama, nrp, pangkat, jabatan, satker, polda, npwp, rekening, bank];
}
class Loaded extends DetailpersonilState {
const Loaded(
String nama,
String nrp,
String pangkat,
String jabatan,
String satker,
String polda,
String npwp,
String rekening,
String bank,
) : super(
nama: nama,
nrp: nrp,
pangkat: pangkat,
jabatan: jabatan,
satker: satker,
polda: polda,
npwp: npwp,
rekening: rekening,
bank: bank);
#override
List<Object> get props =>
[nama, nrp, pangkat, jabatan, satker, polda, npwp, rekening, bank];
}
LoginPage
class LoginPage extends StatefulWidget {
const LoginPage({Key key}) : super(key: key);
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
double width = 0;
double height = 0;
TextEditingController nrpController = TextEditingController();
TextEditingController sandiController = TextEditingController();
final formKey = GlobalKey<FormState>();
DetailpersonilBloc detailPersonilBloc;
GajiBloc gajiBloc;
TunkinBloc tunkinBloc;
bool passwordVisible = false;
#override
void initState() {
super.initState();
checkToken();
}
void checkToken() async {
SharedPreferences pref = await SharedPreferences.getInstance();
if (pref.getString('token') != null) {
detailPersonilBloc = DetailpersonilBloc();
gajiBloc = GajiBloc();
tunkinBloc = TunkinBloc();
detailPersonilBloc.add(Detail());
gajiBloc.add(Gaji());
tunkinBloc.add(Tunkin());
Navigator.push(
context, MaterialPageRoute(builder: (context) => const MainPage()));
}
}
onLogin(DetailpersonilBloc detailPersonilBloc) async {
if (formKey.currentState.validate()) {
var token = await APIService.generateToken(
nrpController.text, sandiController.text);
if (token != null) {
SharedPreferences pref = await SharedPreferences.getInstance();
await pref.setString('token', token.token);
var detail = await APIService.getDetailPersonil(token.token);
await pref.setString('nama', detail.nMPEG);
await pref.setString('NRP', detail.nRP);
await pref.setString('pangkat', detail.nMGOL1);
await pref.setString('jabatan', detail.sEBUTJAB);
await pref.setString('satker', detail.nMSATKER);
await pref.setString('polda', detail.nMUAPPAW);
await pref.setString('NPWP', detail.nPWP);
await pref.setString('rekening', detail.rEKENING);
await pref.setString('bank', detail.nMBANK);
nrpController.clear();
sandiController.clear();
detailPersonilBloc.add(Detail());
Navigator.push(
context, MaterialPageRoute(builder: (context) => const MainPage()));
} else {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Error'),
content: Text('Login Gagal'),
actions: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Tutup'),
),
],
);
},
);
}
}
}
#override
Widget build(BuildContext context) {
var detailPersonilBloc = BlocProvider.of<DetailpersonilBloc>(context);
width = MediaQuery.of(context).size.width;
height = MediaQuery.of(context).size.height;
return Scaffold(
body: Stack(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: const [
Opacity(
opacity: 0.5,
child: Image(
image: AssetImage('images/bg-map-min.png'),
),
),
],
),
SingleChildScrollView(
padding: EdgeInsets.only(top: 100),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 100,
width: width,
child: const Image(
image: AssetImage('images/login-logo.png'),
alignment: Alignment.center,
),
),
Container(
padding: const EdgeInsets.all(15),
child: const Text(
'GAJI DAN TUNKIN',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
Form(
key: formKey,
child: Column(
children: [
Container(
margin: const EdgeInsets.all(20 - 2.6),
child: Card(
elevation: 10,
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Container(
alignment: Alignment.topLeft,
padding: const EdgeInsets.only(bottom: 20),
child: const Text(
'LOGIN',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
Container(
padding: const EdgeInsets.only(bottom: 25),
child: TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan NRP/NIP';
}
return null;
},
controller: nrpController,
decoration: InputDecoration(
labelText: 'NRP/NIP',
hintText: 'Masukkan NRP/NIP',
prefixIcon: Icon(Icons.person,
color: Colors.blue.shade700),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
),
TextFormField(
obscureText: !passwordVisible,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan Kata Sandi';
}
return null;
},
controller: sandiController,
decoration: InputDecoration(
labelText: 'Kata Sandi',
hintText: 'Masukkan Kata Sandi',
prefixIcon: Icon(Icons.lock,
color: Colors.blue.shade700),
suffixIcon: IconButton(
onPressed: () {
setState(() {
passwordVisible = !passwordVisible;
});
},
icon: Icon(
passwordVisible
? Icons.visibility
: Icons.visibility_off,
color: Colors.blue.shade700,
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
)
],
),
),
),
),
ElevatedButton(
onPressed: () async {
await onLogin(detailPersonilBloc);
},
child: const Text('LOGIN'),
style: ElevatedButton.styleFrom(
primary: Colors.blue.shade700,
minimumSize: const Size(200, 40),
),
)
],
),
),
],
),
),
],
),
);
}
}
GajiPage
class GajiPage extends StatefulWidget {
const GajiPage({Key key}) : super(key: key);
#override
_GajiPageState createState() => _GajiPageState();
}
class _GajiPageState extends State<GajiPage> {
double width = 0;
double height = 0;
var currentYear = DateTime.now().year;
var currentMonth = DateTime.now().month;
DetailpersonilBloc detailPersonilBloc;
GajiBloc gajiBloc;
#override
void initState() {
setState(() {
detailPersonilBloc = DetailpersonilBloc();
detailPersonilBloc.add(Detail());
setMonth();
setYear();
gajiBloc = GajiBloc();
gajiBloc.add(Gaji());
});
super.initState();
}
#override
Widget build(BuildContext context) {
var detailPersonilBloc = BlocProvider.of<DetailpersonilBloc>(context);
width = MediaQuery.of(context).size.width;
height = MediaQuery.of(context).size.height;
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Image(
image: const AssetImage('images/header-logo.png'),
width: width / 2,
),
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Color.fromARGB(255, 170, 177, 175),
Color.fromARGB(255, 197, 217, 212)
],
),
),
),
),
body: Stack(
children: [
BlocBuilder<GajiBloc, GajiState>(
builder: (context, state) {
return state is GajiLoaded
? ListView(
children: [
Container(
height: 100,
),
Card(
color: const Color.fromARGB(255, 74, 50, 152),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: const EdgeInsets.all(10),
child: const Text(
'Gaji Bersih',
style: TextStyle(
fontSize: 20,
color: Colors.white,
),
),
),
Container(
margin: const EdgeInsets.all(10),
child: Text(
NumberFormat.currency(
locale: 'en',
symbol: 'RP ',
decimalDigits: 0)
.format(state.bersih),
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 40,
color: Colors.white,
),
),
),
],
),
),
Card(
child: Column(
children: [
Container(
color: const Color.fromARGB(255, 238, 238, 238),
width: width,
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Container(
margin: const EdgeInsets.fromLTRB(
10, 10, 0, 10),
width: (width / 2) - 25,
child: const Text(
'Detail Gaji',
textAlign: TextAlign.start,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 20,
),
),
),
Container(
margin: const EdgeInsets.fromLTRB(
5, 10, 20, 10),
width: (width / 2) - 18,
child: Text(
'${state.bulan} - ${state.tahun}',
textAlign: TextAlign.end,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 20,
),
),
)
],
),
),
...
],
),
),
Container(
height: 50,
),
],
)
: Center(
child: Text(
'Tidak ada data. Data gaji bulan ${state.bulan} belum diproses'),
);
},
),
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Center(
child: BlocBuilder<DetailpersonilBloc, DetailpersonilState>(
builder: (context, state) {
return Card(
child: Row(
children: [
Container(
margin: const EdgeInsets.all(10),
child: const CircleAvatar(
backgroundImage: AssetImage('images/Profpic.PNG'),
radius: 30,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 250,
padding: const EdgeInsets.all(5),
child: Text(
state.nama,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold),
)),
Container(
padding: const EdgeInsets.all(5),
child: Text(
state.nrp,
style: const TextStyle(fontSize: 15),
)),
],
),
GestureDetector(
onTap: () {
detailPersonilBloc.add(Detail());
showModalBottomSheet(
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (context) => detailsBottomSheet(),
);
},
child: const Text(
'DETAILS',
style: TextStyle(color: Colors.blue),
),
)
],
),
);
},
),
),
GestureDetector(
onTap: () {
showModalBottomSheet(
backgroundColor: Colors.transparent,
isScrollControlled: true,
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, StateSetter setState) {
return filterBottomSheet();
},
);
},
);
},
child: Container(
height: 50,
width: width,
decoration: const BoxDecoration(
color: Color.fromARGB(255, 244, 244, 244),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
margin: const EdgeInsets.only(right: 3),
child: const Icon(
Icons.tune,
color: Color.fromARGB(255, 45, 165, 217),
),
),
const Text(
'Filter',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 20,
color: Color.fromARGB(255, 45, 165, 217),
),
),
],
),
),
)
],
)
],
),
);
}
}
Note 2: The "..." is a bunch of code not needed for reproduction.
The answer is surprisingly simple as I would learn from my internship. The reason the Bloc is not updating is because I was not using the context of the application. So when I generated a new Bloc inside my pages, it was "empty". So the solution is to use context.read().add(BlocEvent()) or create a new bloc with BlocProvider.of(context) then add the event. Basically the bloc has to be provided with the original context of the application.

Setstate() not working to update suffixIcon inside AlertDialog ? - Flutter

I'm trying to update the suffixIcon in Form for TextFormField's InputDecoration , to check the length of the giving String by onChange function ,but no update . In add I did test on Textcontroller to give it value when the length >= 8 it's worked ,I wish I give a good idea for the issue . thanks in advance for your solution .
import 'package:flutter/material.dart';
bulidDialogChangePass() {
return DialogPass();
}
class DialogPass extends StatefulWidget {
const DialogPass({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() {
return DialogPassState();
}
}
class DialogPassState extends State<DialogPass> {
TextEditingController _fPasswordValue = TextEditingController();
TextEditingController _sPasswordValue = TextEditingController();
final _formKey = GlobalKey<FormState>();
ValueChanged<String>? onChanged;
bool _showIcons = false;
bool _startValidateFPass = false;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 25.0),
child: Wrap(
children: [
Container(
child: Row(
children: [
Text("Password :"),
TextButton(
onPressed: () {
_showDialog(context);
},
child: Wrap(
direction: Axis.vertical,
alignment: WrapAlignment.end,
children: [Text("Change")],
),
)
],
),
),
],
),
);
}
_showDialog(BuildContext context) async {
var size = MediaQuery.of(context).size;
return await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Stack(
clipBehavior: Clip.none,
children: <Widget>[
Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
controller: _fPasswordValue,
onChanged: (value) {_onChange(_fPasswordValue.text);},
decoration: InputDecoration(
labelText: "New Password",
suffixIcon: _showIcons && _startValidateFPass ? _setIcon() :null,
),
),
),
Padding(
padding: EdgeInsets.all(8.0),
child: TextFormField(
controller: _sPasswordValue,
decoration: InputDecoration(
labelText: "Confirm Password",
suffixIcon: _showIcons && _startValidateFPass ? _setIcon() :null, // I will change to check aothers soon
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Wrap(
children: [
OutlinedButton(
style: OutlinedButton.styleFrom(
fixedSize: Size(size.width / 2 - 10, 20),
padding: EdgeInsets.symmetric(horizontal: 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
),
onPressed: () {},
child: Text("CANCEL",
style: TextStyle(
fontSize: 14,
letterSpacing: 2.2,
color: Colors.black)),
),
OutlinedButton(
style: OutlinedButton.styleFrom(
fixedSize: Size(size.width / 2 - 10, 20),
backgroundColor: Colors.blue[400],
primary: Colors.black,
padding: EdgeInsets.symmetric(horizontal: 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
),
onPressed: () {},
child: Text("Save",
style: TextStyle(
fontSize: 14,
letterSpacing: 2.2,
color: Colors.black)),
),
],
),
),
],
),
),
],
),
);
});
}
_onChange(String value) {
setState(() {
_startValidateFPass = true;
if (value.length >= 8) {
_sPasswordValue.text = "test"; // test to check setState
_showIcons = true;
}
});
}
Icon _setIcon() {
Icon icon;
if(_showIcons){
_sPasswordValue.text = "test"; // test to check setState
print('dfsdfsd');
icon = Icon(
Icons.done_all,
color: Colors.green,
);
}else{
icon = Icon(
Icons.highlight_off,
color: Colors.red,
);
}
return icon;
}
}
Make another stateful widget and make Alertdialog as its child or use stateful builder to accomplish this as
showDialog(
context: context,
builder: (context) {
String contentText = "Your Dialog";
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: Text("Title"),
content: Text(contentText),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context),
child: Text("Cancel"),
),
TextButton(
onPressed: () {
setState(() {
contentText = "Changed state";
});
},
child: Text("Change"),
),
],
);
},
);
},
);

fix this flutter CHATVIEW issue. i can't seem to get past it for days

Any time i send a message, the messages just keep going down and does not behave like a chatting interface.
reverse = true, didnt work
Listview , gives error
Singlechildscrollview, gives error too
I cant continue coding further
Class ChatPage extends StatefulWidget {
final String groupId;
final String userName;
final String groupName;
ChatPage({this.groupId, this.userName, this.groupName});
#override
_ChatPageState createState() => _ChatPageState();
}
class _ChatPageState extends State<ChatPage> {
Stream<QuerySnapshot> _chats;
TextEditingController messageEditingController = new TextEditingController();
Widget _chatMessages() {
return Padding(
padding: EdgeInsets.all(1.0),
child: StreamBuilder(
stream: _chats,
builder: (context, snapshot) {
return snapshot.hasData
? Column(
children: [
Expanded(
child: ListView.builder(
shrinkWrap: true,
//reverse: true,
padding: EdgeInsets.all(10.0),
//padding: EdgeInsets.symmetric(horizontal: 0.0, vertical: 90.0),
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
return MessageTile(
message: snapshot
.data.documents[index].data["message"],
sender:
snapshot.data.documents[index].data["sender"],
sentByMe: widget.userName ==
snapshot.data.documents[index].data["sender"],
);
}),
),
Container(
alignment: Alignment.bottomCenter,
width: MediaQuery.of(context).size.width,
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 15.0, vertical: 10.0),
color: Colors.deepPurple,
child: Row(
children: <Widget>[
Expanded(
child: TextField(
controller: messageEditingController,
style: TextStyle(color: Colors.white),
decoration: InputDecoration(
hintText: "Send a message ...",
hintStyle: TextStyle(
color: Colors.white70,
fontSize: 16,
),
border: InputBorder.none),
),
),
SizedBox(width: 12.0),
GestureDetector(
onTap: () {
_sendMessage();
},
child: Container(
height: 50.0,
width: 50.0,
decoration: BoxDecoration(
color: Colors.pink,
borderRadius: BorderRadius.circular(50)),
child: Center(
child:
Icon(Icons.send, color: Colors.white)),
),
)
],
),
),
)
],
)
: Container();
},
),
);
}
_sendMessage() {
if (messageEditingController.text.isNotEmpty) {
Map<String, dynamic> chatMessageMap = {
"message": messageEditingController.text,
"sender": widget.userName,
'time': DateTime.now().millisecondsSinceEpoch,
};
DatabaseService().sendMessage(widget.groupId, chatMessageMap);
setState(() {
messageEditingController.text = "";
});
}
}
#override
void initState() {
super.initState();
DatabaseService().getChats(widget.groupId).then((val) {
// print(val);
setState(() {
_chats = val;
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
//bluegradient below
Color(0xFF3366FF),
Color(0xFFCC00CC),
],
),
),
),
title: Text(widget.groupName,
style: TextStyle(
fontSize: 27.0,
fontWeight: FontWeight.bold,
color: Colors.white,
fontFamily: 'Quicksand',
)),
centerTitle: true,
backgroundColor: Colors.black87,
elevation: 0.0,
),
body: Stack(
children: <Widget>[
_chatMessages(),
// Container(),
],
),
);
}
}
You have this issue because at the moment you send your message and want to scroll to the bottom your ListView has not been rebuild yet and it does not contains your latest message. The only solution I have found for now is to scroll above the expected limit of your list as you know you will have another element to display.
Here is how I have modified your code:
_sendMessage() {
if (messageEditingController.text.isNotEmpty) {
Map<String, dynamic> chatMessageMap = {
"message": messageEditingController.text,
"sender": widget.userName,
'time': DateTime.now().millisecondsSinceEpoch,
};
DatabaseService().sendMessage(widget.groupId, chatMessageMap);
// Scroll to bottom of list programmatically
_scrollController.jumpTo(_scrollController.position.maxScrollExtent +
MediaQuery.of(context).size.height);
setState(() => messageEditingController.text = "");
}
}
I have added a ScrollController to your ListView and scrolling to its current limit (with _scrollController.position.maxScrollExtent) and adding the height of your phone screen to ensure that you will see the last message. The scrolling automatically correct itself right after so you will be able to see your newly send message.
You can see and test my entire code on DartPad