How do I keep a variable value after UI state change? - flutter

So basically, I have an ImagePicker widget that empties every time my view changes in a browser(flutter web):
But once I change the size of the browser window, the image dissappears:
So I was wondering if there's a way to keep value of the variable that contains the string image.
This is the function that gets the image:
static Future<Uint8List?> webPickImage() async {
final picker = ImagePicker();
var file = await picker.pickImage(source: ImageSource.gallery);
if (file != null) {
return await file.readAsBytes();
} else {
return null;
}
}
As you can see, this returns an Uint8List which then is emited in a BLoC/cubit state management class variable
void userWebImageChanged() async {
final newWebImage = await CommonFunction.webPickImage();
emit(
state.copyWith(
webUserImage: newWebImage,
),
);
}
State management class:
So it appears that webUserImage loses it's value when the browser window changes size:
part of 'sign_up_cubit.dart';
class SignUpState extends Equatable {
const SignUpState(
{
this.webUserImage
});
final Uint8List? webUserImage;
#override
List<Object?> get props => [
webUserImage
];
SignUpState copyWith(
{
Uint8List? webUserImage
}) {
return SignUpState(
webUserImage: webUserImage ?? this.webUserImage);
}
}
Widget(read comments):
class SignUpForm extends StatelessWidget {
const SignUpForm({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => SignUpCubit(),
child: BlocConsumer<SignUpCubit, SignUpState>(
builder: (context, state) => ResponsiveRowColumn(
rowMainAxisAlignment: MainAxisAlignment.spaceEvenly,
rowPadding: const EdgeInsets.all(10),
columnPadding: const EdgeInsets.all(10),
layout: ResponsiveWrapper.of(context).isSmallerThan(DESKTOP)
? ResponsiveRowColumnType.COLUMN
: ResponsiveRowColumnType.ROW,
children: [
ResponsiveRowColumnItem(
rowFlex: 1,
child: SizedBox(
width: 350,
child: Column(
children: [
Wrap(
runSpacing: 10,
alignment: WrapAlignment.center,
children: [
ResponsiveVisibility(
hiddenWhen: const [
Condition.smallerThan(name: TABLET)
],
//BLoC
child: BlocBuilder<SignUpCubit, SignUpState>(
buildWhen: (previous, current) =>
previous.webUserImage !=
current.webUserImage,
builder: (cubitContext, state) {
return GestureDetector(
onTap: () async {
cubitContext
.read<SignUpCubit>()
.userWebImageChanged();
},
child: Container(
margin: const EdgeInsets.all(10),
width: 135,
height: 135,
decoration: BoxDecoration(
borderRadius:
const BorderRadius.all(
Radius.circular(10)),
border: Border.all(
color: Theme.of(context)
.primaryColorDark,
width: 1,
),
color: Colors.white),
child: state.webUserImage == null
? Center(
child: Icon(
Icons.add,
color: Theme.of(context)
.primaryColorDark,
size: 50,
),
)
: ClipRRect(
child: Image.memory(
// This is the state variable that sets and holds the image:
state.webUserImage!),
borderRadius:
const BorderRadius.all(
Radius.circular(11)),
),
),
);
},
),
//...

Related

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 display the saved state of a button when the page opens?

I have three buttons. The maximum that I can select (activate) is only one button. When switching buttons, I have activated should be true, and not activated - false. I write these values ​​to SharedPreferences for each button, store true or false. When I open the pages all the buttons are gray out (they are not selected). I need to save the button state that it was selected and display it when the page is opened. For example, I just need if the variable isVoltageAC = true, then the AC button will immediately turn purple when the page is opened. How to do it?
enum VoltageMode {
ac,
dc,
all,
}
class FilterDialog extends StatefulWidget {
const FilterDialog({
Key? key,
}) : super(key: key);
#override
State<FilterDialog> createState() => _FilterDialogState();
}
class _FilterDialogState extends State<FilterDialog> {
VoltageMode? selectedMode;
#override
Widget build(BuildContext context) {
return BlocBuilder<MapPreferencesCubit, MapPreferencesState>(
builder: (context, statePreferences) {
final MapPreferencesCubit mapPreferencesCubit =
BlocProvider.of<MapPreferencesCubit>(context);
if (statePreferences is MapPreferencesInitial) {
mapPreferencesCubit.getPreferences();
}
if (statePreferences is MapPreferencesLoaded) {
return BlocBuilder<MapfilterCubit, MapFilterState>(
builder: (context, stateFilter) {
final MapfilterCubit mapFilterCubit =
BlocProvider.of<MapfilterCubit>(context);
if (stateFilter is MapFilterInitial) {
mapFilterCubit.getFilter();
}
if (stateFilter is MapFilterLoaded) {
bool isVoltageAC = stateFilter.mapFilter.voltagePowerAC;
bool isVoltageDC = stateFilter.mapFilter.voltagePowerDC;
bool isVoltageAll = stateFilter.mapFilter.voltagePowerAll;
return SingleChildScrollView(
child: Dialog(
insetPadding: const EdgeInsets.only(
top: 121, left: 24, right: 24, bottom: 60),
child: Container(
decoration: const BoxDecoration(
color: constants.Colors.greyDark,
borderRadius: BorderRadius.all(Radius.circular(24)),
),
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 26, 0, 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [,
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 21),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () => setState(() {
selectedMode = VoltageMode.ac;
}),
child: _buttonVoltage(
'AC', selectedMode == VoltageMode.ac),
),
const SizedBox(width: 16),
GestureDetector(
onTap: () => setState(() {
selectedMode = VoltageMode.dc
}),
child: _buttonVoltage(
'DC', selectedMode == VoltageMode.dc),
),
const SizedBox(width: 16),
GestureDetector(
onTap: () => setState(() {
selectedMode = VoltageMode.all;
}),
child: _buttonVoltage(
'All', selectedMode == VoltageMode.all),
),
],
),
),
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 21),
child: DefaultButtonGlow(
text: 'Done',
onPressed: () {
Navigator.pop(context);;
mapFilterCubit
.setFilter(
MapFilter(
voltagePowerAC:
selectedMode == VoltageMode.ac,
voltagePowerDC:
selectedMode == VoltageMode.dc,
voltagePowerAll:
selectedMode == VoltageMode.all,
),
)
},
),
Widget _buttonVoltage(String nameButton, bool isActive) => Container(
padding: const EdgeInsets.symmetric(vertical: 11),
height: 40,
width: 87,
decoration: BoxDecoration(
color: isActive
? constants.Colors.purpleMain
: constants.Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: isActive ? Colors.transparent : constants.Colors.greyDark,
),
boxShadow: [
BoxShadow(
color: isActive
? constants.Colors.purpleMain.withOpacity(0.34)
: Colors.transparent,
blurRadius: 10,
spreadRadius: 2,
offset: const Offset(0.0, 1.0)),
],
),
alignment: Alignment.center,
child:
Text(nameButton, style: constants.Styles.smallBoldTextStyleWhite),
);
cubit
Future setFilter(MapFilter mapFilter) async {
await _repository.setFilter(mapFilter: mapFilter);
final MapFilter? filter = await _repository.getFilter();
emit(MapFilterLoaded(filter!));
return filter;}
sharedpreferences
Future setFilter({required MapFilter mapFilter}) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString(_filterName, jsonEncode(mapFilter.toJson()));
}
you can read this values from SharedPreferences in initState of page. In this way your default value is ready when page loaded.
then make everything after SingleChildScrollView in separate widget like this:
Widget _buildBody(){
return SingleChildScrollView(
child: Dialog(
...
),
);
}
and pass this widget in your bloc builder after all if statement by default.
then do this:
void initState() {
super.initState();
final SharedPreferences prefs = await SharedPreferences.getInstance();
var result = prefs.readData(_filterName);
if (result != null) {
MapFilter mapFilter = jsonDecode(mapFilter.fromJson(result));
if (mapFilter.voltagePowerAC){
selectedMode = VoltageMode.ac;
}else if (mapFilter.voltagePowerDC){
selectedMode = VoltageMode.dc;
} else {
selectedMode = VoltageMode.all;
}
}
}

How to remove item from cart in flutter/ dart

How to remove cart items from a list in flutter when you have a seperate list widget ?
I have three files that contain the following code.
carts.dart => where I display all the list items
cart_list_item => where I have created a widget.
api_services.dart => from where I am accessing the functions.
It was working when the cart list widget and cart list was in a single file.
but to increment cart quantity I had to separate them.
CARTS.DART
class _CartsListState extends State<CartsList> {
List cartList = [];
getProducts() async {
var resp = await http.get(
Uri.parse("https://abc"));
cartList.addAll(jsonDecode(resp.body));
setState(()=>{});
return jsonDecode(resp.body);
}
#override
void initState() {
super.initState();
getProducts();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(10),
child: FutureBuilder(
future: getProducts(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: cartList.length,
itemBuilder: (BuildContext context, int index) {
var path = "https://abc";
var img = cartList[index]['image'] ?? 'default.png';
return MyCartListItem(
cartName: cartList[index]['english_name'],
cartQuantity: 2,
cartImage: path + img,
cartPrice: cartList[index]['mrp'].toString(),
cartIndex: 5);
},
);
} else {
return const LinearProgressIndicator();
}
},
),
),
);
}
}
cart_list_item.dart
class MyCartListItem extends StatefulWidget {
const MyCartListItem(
{Key? key,
required this.cartName,
required this.cartQuantity,
required this.cartImage,
required this.cartPrice,
required this.cartIndex})
: super(key: key);
final String cartName;
final int cartQuantity;
final String cartImage;
final String cartPrice;
final int cartIndex;
#override
State<MyCartListItem> createState() => _MyCartListItemState();
}
#override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(10.5),
child: Row(children: [
const SizedBox(
width: 10,
),
SizedBox(
width: 70,
height: 70,
child: Image.network(widget.cartImage),
),
const SizedBox(
width: 50,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
// ignore: prefer_const_literals_to_create_immutables
children: [
Container(
child: Text(
widget.cartName,
style: const TextStyle(
overflow: TextOverflow.clip,
),
),
),
const SizedBox(
width: 10,
),
],
),
const SizedBox(
height: 7,
),
Text(
"Rs. ${widget.cartPrice}",
style: const TextStyle(
fontWeight: FontWeight.bold, color: Colors.grey),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
InkWell(
onTap: () {
// setState() {
// _itemCount--;
// print(_itemCount);
// }
},
child: Container(
height: 30,
width: 30,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: Colors.deepOrange[50],
),
child: const Icon(
CupertinoIcons.minus,
)),
),
const SizedBox(
width: 10,
),
Text(_itemCount.toString()),
const SizedBox(
width: 10,
),
InkWell(
// onTap: () {
// setState() => {_itemCount++};
// },
child: Container(
height: 30,
width: 30,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: Colors.deepOrange[50],
),
child: const Icon(
CupertinoIcons.plus,
)),
),
const SizedBox(
width: 15,
),
InkWell(
onTap: () {
ApiServices.removeCartItem(0);
// setState(() {});
final snackBarData = SnackBar(
behavior: SnackBarBehavior.floating,
content: Row(
children: const [
Icon(Icons.shopping_bag),
SizedBox(
width: 10,
),
Text('Product removed from cart !!!'),
],
),
);
ScaffoldMessenger.of(context).showSnackBar(snackBarData);
},
child: const Icon(
CupertinoIcons.trash,
color: Colors.red,
),
)
],
)
],
)
]),
),
);
}
}
api services.dart*
static removeCartItem(int indexNumber) async {
cartList.removeAt(indexNumber);
}
You need to update your view because data in list was changed. If you are using setState for in, implement callback in CartListItem that allows you to handle item deletion and update view in this callback. It should be looks like:
class MyCartListItem extends StatefulWidget {
const MyCartListItem(
{Key? key,
required this.cartName,
required this.cartQuantity,
required this.cartImage,
required this.cartPrice,
required this.cartIndex,
required this.onItemRemoved,
}) : super(key: key);
final String cartName;
final int cartQuantity;
final String cartImage;
final String cartPrice;
final int cartIndex;
final Function onItemRemoved;
#override
State<MyCartListItem> createState() => _MyCartListItemState();
}
And in CartListItem:
onTap: () => widget.onItemRemoved(index);
its already answered, but if you still had i problem, i try to make it clear, you can ask later if not working.
in you listViewBuilder :
return ListView.builder(
itemCount: cartList.length,
itemBuilder: (BuildContext context, int index) {
var path = "https://abc";
var img = cartList[index]['image'] ?? 'default.png';
return MyCartListItem(
cartName: cartList[index]['english_name'],
cartQuantity: 2,
cartImage: path + img,
cartPrice: cartList[index]['mrp'].toString(),
cartIndex: 5,
//------- this line -------------
removeFunction:(){
ApiServices.removeCartItem(index);
}
);
},
);
in your Cardlistitem :
class MyCartListItem extends StatefulWidget {
const MyCartListItem(
{Key? key,
required this.cartName,
required this.cartQuantity,
required this.cartImage,
required this.cartPrice,
required this.cartIndex,
required this.removeFuntion,
}) : super(key: key);
final String cartName;
final int cartQuantity;
final String cartImage;
final String cartPrice;
final int cartIndex;
final Function removeFunction;
#override
State<MyCartListItem> createState() => _MyCartListItemState();
}
.... Rest OF code
// and here :
InkWell(
onTap: () {
//------- THIS LINE -----:
widget.removeFunction;
//-----------------------
// setState(() {});
final snackBarData = SnackBar(
behavior: SnackBarBehavior.floating,
content: Row(
children: const [
Icon(Icons.shopping_bag),
SizedBox(
width: 10,
),
Text('Product removed from cart !!!'),
],
),
);
ScaffoldMessenger.of(context).showSnackBar(snackBarData);
},
child: const Icon(
CupertinoIcons.trash,
color: Colors.red,
),
)

Translate chat message based on language that user picks from drop down list

I am trying to get messages translated in real-time in the chat portion of my app depending on the language that the user picks in real-time. For example, if the user only speaks Spanish but messages from the user that they are chatting with are in English, then the user can select 'Spanish' from the dropdown list and all messages that have already been received and all future messages that they will receive will be translated into Spanish. I am capturing the sent message and its translation in each language in firebase but not sure how to get the messages to actually translate on the frontend. Any help would be much appreciated. Thank you in advance!
chat.dart
class Chat extends StatelessWidget {
final String? peerId;
final String? peerAvatar;
final String? name;
Chat({Key? key, this.peerId, this.peerAvatar, this.name}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: SkapeColors.pageBackgroundFifth,
resizeToAvoidBottomInset: true,
appBar: AppBar(
iconTheme: IconThemeData(
color: Colors.white,
),
backgroundColor: SkapeColors.pageBackground,
title: Text(
name!,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: 'Brand-Bold', fontWeight: FontWeight.bold, color: Colors.white, fontSize: 25),
),
centerTitle: true,
),
body: ChatScreen(
peerId: peerId,
peerAvatar: peerAvatar,
name: name,
),
);
}
}
class ChatScreen extends StatefulWidget {
final String? peerId;
final String? peerAvatar;
final String? name;
ChatScreen({Key? key, this.peerId, this.peerAvatar, this.name})
: super(key: key);
#override
State createState() =>
ChatScreenState(peerId: peerId, peerAvatar: peerAvatar);
}
class ChatScreenState extends State<ChatScreen> {
ChatScreenState({Key? key, this.peerId, this.peerAvatar, this.name});
String? peerId;
String? peerAvatar;
String? name;
String? id;
String? language1 = Translations.languages.first;
String? language2 = Translations.languages.first;
final translator = GoogleTranslator();
static final _apiKey = 'hidden';
List<QueryDocumentSnapshot> listMessage = new List.from([]);
int _limit = 20;
int _limitIncrement = 20;
String groupChatId = "";
SharedPreferences? prefs;
File? imageFile;
bool isLoading = false;
bool isShowSticker = false;
String imageUrl = "";
final TextEditingController textEditingController = TextEditingController();
final ScrollController listScrollController = ScrollController();
final FocusNode focusNode = FocusNode();
_scrollListener() {
if (listScrollController.offset >=
listScrollController.position.maxScrollExtent &&
!listScrollController.position.outOfRange) {
setState(() {
_limit += _limitIncrement;
});
}
}
#override
void initState() {
super.initState();
focusNode.addListener(onFocusChange);
listScrollController.addListener(_scrollListener);
readLocal();
}
readLocal() async {
prefs = await SharedPreferences.getInstance();
id = await getUserID();
if (id.hashCode <= peerId.hashCode) {
groupChatId = '$id-$peerId';
} else {
groupChatId = '$peerId-$id';
}
FirebaseFirestore.instance
.collection('users')
.doc(id)
.update({'chattingWith': peerId});
setState(() {});
}
static Future<String> translate(String message, String toLanguageCode) async {
final response = await http.post(
Uri.parse('https://translation.googleapis.com/language/translate/v2?target=$toLanguageCode&key=$_apiKey&q=$message'),
);
if (response.statusCode == 200) {
final body = json.decode(response.body);
final translations = body['data']['translations'] as List;
final translation = translations.first;
return HtmlUnescape().convert(translation['translatedText']);
} else {
throw Exception();
}
}
static Future<String> translate2(
String message, String fromLanguageCode, String toLanguageCode) async {
final translation = await GoogleTranslator().translate(
message,
from: fromLanguageCode,
to: toLanguageCode,
);
return translation.text;
}
Future<void> onSendMessage(String content, int type) async {
// type: 0 = text, 1 = image, 2 = sticker
if (content.trim() != '') {
textEditingController.clear();
var documentReference = FirebaseFirestore.instance
.collection('messages')
.doc(groupChatId)
.collection(groupChatId)
.doc(DateTime.now().millisecondsSinceEpoch.toString());
FirebaseFirestore.instance.runTransaction((transaction) async {
transaction.set(
documentReference,
{
'idFrom': id,
'idTo': peerId,
'timestamp': DateTime.now().millisecondsSinceEpoch.toString(),
'content': content,
'translated': {
'english': await translate(content, 'en'),
'spanish': await translate(content, 'es'),
'german': await translate(content, 'de'),
'french': await translate(content, 'fr'),
'russian': await translate(content, 'ru'),
'italian': await translate(content, 'it'),
'selectedTranslation': language1,
},
'type': type
},
);
});
listScrollController.animateTo(0.0,
duration: Duration(milliseconds: 300), curve: Curves.easeOut);
try {
String body = content;
if (content.contains("firebasestorage")) {
body = "Image";
}
var tempResp = await getUserInformation();
await sendNotificationToUser(
peerId, "New message from " + tempResp["fullName"], body);
} catch (e) {
print(e);
}
} else {
Fluttertoast.showToast(
msg: 'Nothing to send. Please insert your message',
backgroundColor: Colors.white24,
textColor: SkapeColors.colorPrimary);
}
}
Widget buildItem(int index, DocumentSnapshot document) {
String language1 = Translations.languages.first;
String language2 = Translations.languages.first;
if (document != null) {
if (document.get('idFrom') == id) {
// Right (my message)
return Row(
children: <Widget>[
document.get('type') == 0
// Text
? Container(
child: TranslationWidget(
message: document.get('content'),
fromLanguage: language1,
toLanguage: language1,
builder: (translatedMessage)=> MessageWidget(message: document.get('content'), translatedMessage: document.get('content'), isMe: true),
),
padding: EdgeInsets.fromLTRB(5.0, 5.0, 5.0, 5.0),
width: 215.0,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary,
borderRadius: BorderRadius.circular(8.0)),
margin: EdgeInsets.only(
bottom: isLastMessageRight(index) ? 20.0 : 10.0,
right: 10.0),
)
: document.get('type') == 1
// Image
? Container(
child: OutlinedButton(
child: Material(
child: Image.network(
document.get("content"),
loadingBuilder: (BuildContext context,
Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.all(
Radius.circular(8.0),
),
),
width: 200.0,
height: 200.0,
child: Center(
child: CircularProgressIndicator(
color: SkapeColors.colorPrimary,
value: loadingProgress
.expectedTotalBytes !=
null &&
loadingProgress
.expectedTotalBytes !=
null
? loadingProgress
.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
),
);
},
width: 200.0,
height: 200.0,
fit: BoxFit.cover,
),
borderRadius:
BorderRadius.all(Radius.circular(8.0)),
clipBehavior: Clip.hardEdge,
),
onPressed: () {
print("here");
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FullPhoto(
url: document.get('content'),
),
),
);
},
// Sticker
: Container(
child: Image.asset(
'images/${document.get('content')}.gif',
width: 100.0,
height: 100.0,
fit: BoxFit.cover,
),
margin: EdgeInsets.only(
bottom: isLastMessageRight(index) ? 20.0 : 10.0,
right: 10.0),
),
],
mainAxisAlignment: MainAxisAlignment.end,
);
} else {
// Left (peer message)
return Container(
child: Column(
children: <Widget>[
Row(
children: <Widget>[
isLastMessageLeft(index)
? Material(
child: Image.network(
peerAvatar!,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
color: SkapeColors.colorPrimary,
value: loadingProgress.expectedTotalBytes !=
null &&
loadingProgress.expectedTotalBytes !=
null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
);
},
errorBuilder: (context, object, stackTrace) {
return Image.asset(
'images/user_icon.png',
height: 50,
width: 50,
);
},
width: 40,
height: 40,
fit: BoxFit.cover,
),
borderRadius: BorderRadius.all(
Radius.circular(35.0),
),
clipBehavior: Clip.hardEdge,
)
: Container(width: 35.0),
document.get('type') == 0
? Container(
child:
TranslationWidget(
message: document.get('content'),
fromLanguage: language1,
toLanguage: language1,
builder: (translatedMessage)=> MessageWidget(message: document.get('content'), translatedMessage: document.get('content'), isMe: false),
),
padding: EdgeInsets.fromLTRB(10.0, 5.0, 5.0, 5.0),
width: 215.0,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(8.0)),
margin: EdgeInsets.only(
bottom: isLastMessageRight(index) ? 20.0 : 10.0,
right: 10.0),
).paddingOnly(left: 12)
: document.get('type') == 1
? Container(
child: TextButton(
child: Material(
child: Image.network(
document.get('content'),
loadingBuilder: (BuildContext context,
Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) return child;
return Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.all(
Radius.circular(8.0),
),
),
width: 200.0,
height: 200.0,
child: Center(
child: CircularProgressIndicator(
color: SkapeColors.colorPrimary,
value: loadingProgress
.expectedTotalBytes !=
null &&
loadingProgress
.expectedTotalBytes !=
null
? loadingProgress
.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
),
),
);
},
width: 200.0,
height: 200.0,
fit: BoxFit.cover,
),
borderRadius:
BorderRadius.all(Radius.circular(8.0)),
clipBehavior: Clip.hardEdge,
),
onPressed: () {
},
)
: Container(
),
],
),
)
],
crossAxisAlignment: CrossAxisAlignment.start,
),
margin: EdgeInsets.only(bottom: 2.0),
);
}
} else {
return SizedBox.shrink();
}
}
bool isLastMessageLeft(int index) {
if ((index > 0 && listMessage[index - 1].get('idFrom') == id) ||
index == 0) {
return true;
} else {
return false;
}
}
bool isLastMessageRight(int index) {
if ((index > 0 && listMessage[index - 1].get('idFrom') != id) ||
index == 0) {
return true;
} else {
return false;
}
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: ()=> Future.value(true),
child: Stack(
children: <Widget>[
Column(
children: <Widget>[
// List of messages
buildListMessage(),
// Input content
buildInput(),
],
),
// Loading
buildLoading()
],
),
// onWillPop: onBackPress,
);
}
Widget buildLoading() {
return Positioned(
child: isLoading ? const Loading() : Container(),
);
}
Widget buildInput() {
return Container(
child: SingleChildScrollView(
child: Column(
children: [
buildTitle().paddingBottom(10),
Row(
),
SizedBox(width: 10,),
Flexible(
child: Container(
child: TextField(
cursorColor: SkapeColors.colorPrimary,
autocorrect: true,
onSubmitted: (value) {
onSendMessage(textEditingController.text, 0);
},
style: TextStyle(color: Colors.white, fontSize: 18.0),
controller: textEditingController,
decoration: InputDecoration.collapsed(
hintText: 'Send Message....',
hintStyle: TextStyle(color: SkapeColors.colorTextSemiLight),
),
focusNode: focusNode,
),
),
),
// Button send message
color: SkapeColors.pageBackground,
),
],
),
],
),
),
width: double.infinity,
height: 155.0,
// height: 100,
);
}
Widget buildListMessage() {
return Flexible(
child: groupChatId.isNotEmpty
? StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('messages')
.doc(groupChatId)
.collection(groupChatId)
.orderBy('timestamp', descending: true)
.limit(_limit)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
listMessage.addAll(snapshot.data!.docs);
return ListView.builder(
padding: EdgeInsets.all(10.0),
itemBuilder: (context, index) => buildItem(index, snapshot.data!.docs[index]),
itemCount: snapshot.data?.docs.length,
reverse: true,
controller: listScrollController,
);
} else {
return Center(
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(SkapeColors.colorPrimary),
),
);
}
},
)
: Center(
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation<Color>(SkapeColors.colorPrimary),
),
),
);
}
Widget buildTitle() => TitleWidget(
language1: language1,
onChangedLanguage1: (newLanguage) => setState(() {
language1 = newLanguage;
}), key: ValueKey(DropDownWidget),
);
}
TitleWidget.dart
import 'package:flutter/material.dart';
import '../screens/messaging/chatWidgets/DropDownWidget.dart';
class TitleWidget extends StatelessWidget {
final String? language1;
final ValueChanged<String?> onChangedLanguage1;
const TitleWidget({
required this.language1,
required this.onChangedLanguage1,
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) => Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.translate, color: Colors.grey, size: 30,),
SizedBox(width: 12,),
DropDownWidget(
value: language1??"",
onChangedLanguage: onChangedLanguage1, key: key!,
),
],
);
}
translations.dart
class Translations {
static final languages = <String>[
'English',
'Spanish',
'French',
'German',
'Italian',
'Russian'
];
static String getLanguageCode(String language) {
switch (language) {
case 'English':
return 'en';
case 'French':
return 'fr';
case 'Italian':
return 'it';
case 'Russian':
return 'ru';
case 'Spanish':
return 'es';
case 'German':
return 'de';
default:
return 'en';
}
}
}
MessageWidget.dart
import 'package:flutter/material.dart';
class MessageWidget extends StatelessWidget {
final String? message;
final String? translatedMessage;
final bool isMe;
const MessageWidget({
required this.message,
required this.translatedMessage,
required this.isMe,
});
#override
Widget build(BuildContext context) {
final radius = Radius.circular(4);
final borderRadius = BorderRadius.all(radius);
return Row(
//To align at different positions based on if message is from the user or not
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
// padding: EdgeInsets.only(right: 10),
// margin: EdgeInsets.only(right: 10),
constraints: BoxConstraints(maxWidth: 190),
decoration: BoxDecoration(
color: isMe ? Theme.of(context).colorScheme.secondary : Colors.grey,
borderRadius: isMe
? borderRadius.subtract(BorderRadius.only(bottomRight: radius))
: borderRadius.subtract(BorderRadius.only(bottomLeft: radius)),
),
child: buildMessage(),
),
],
);
}
Widget buildMessage() => Column(
crossAxisAlignment:
isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
// Text(
// message,
// style: TextStyle(
// color: isMe ? Colors.black54 : Colors.white70,
// fontSize: 14,
// ),
// textAlign: isMe ? TextAlign.end : TextAlign.start,
// ),
Text(
translatedMessage!,
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.start,
),
],
);
}
TranslationWidget.dart
class TranslationWidget extends StatefulWidget {
final String? message;
final String? fromLanguage;
final String? toLanguage;
final Widget Function(String? translation) builder;
const TranslationWidget({
required this.message,
required this.fromLanguage,
required this.toLanguage,
required this.builder,
Key? key,
}) : super(key: key);
#override
_TranslationWidgetState createState() => _TranslationWidgetState();
}
class _TranslationWidgetState extends State<TranslationWidget> {
String? translation;
#override
Widget build(BuildContext context) {
// final fromLanguageCode = Translations.getLanguageCode(widget.fromLanguage);
final toLanguageCode = Translations.getLanguageCode(widget.toLanguage!);
return FutureBuilder(
future: TranslationApi.translate(widget.message!, toLanguageCode),
//future: TranslationApi.translate2(
// widget.message, fromLanguageCode, toLanguageCode),
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return buildWaiting();
default:
if (snapshot.hasError) {
translation = 'Could not translate due to Network problems';
} else {
translation = snapshot.data!;
}
return widget.builder(translation??"");
}
},
);
}
Widget buildWaiting() =>
translation == null ? Container() : widget.builder(translation??"");
}

getx obx not updating avatar image - Flutter GetX

what I want to achieve is to change the image in CircleAvatar when I'm selecting an image, here is the code:
ProfileController:
class ProfileController extends GetxController {
final TextEditingController emailController = TextEditingController();
final ImagePicker _picker = ImagePicker();
Rx<String?> avatarPath = null.obs;
avatarFromCamera() async {
var localAvatar = await _picker.pickImage(
source: ImageSource.camera, imageQuality: 50
);
if (localAvatar != null) {
avatarPath = localAvatar.path.obs;
update();
}
}
avatarFromGallery() async {
var localAvatar = await _picker.pickImage(
source: ImageSource.gallery, imageQuality: 50
);
if (localAvatar != null) {
avatarPath = localAvatar.path.obs;
update();
}
}
String? emailValidator(String? value) {
if (value == null || value.isEmpty) {
return null;
}
if (!EmailValidator.validate(value, false)) {
return 'Invalid email address';
}
}
#override
void onClose() {
emailController.dispose();
super.onClose();
}
String? emailValidator(String? value) {
if (value == null || value.isEmpty) {
return null;
}
if (!EmailValidator.validate(value, false)) {
return 'Invalid email address';
}
}
void save(GlobalKey<FormState> profileFormKey) {
if (profileFormKey.currentState!.validate()) {
print('valid');
}
}
}
and here is the ProfileScreen widget:
lass ProfileScreen extends StatelessWidget {
final ProfileController _profileController = Get.put<ProfileController>(ProfileController());
GlobalKey<FormState> profileFormKey = GlobalKey<FormState>();
ProfileScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Update user details'),
),
body: SingleChildScrollView(
child: Form(
key: profileFormKey,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(30.0),
child: TextFormField(
keyboardType: TextInputType.text,
controller: _profileController.emailController,
decoration: const InputDecoration(
labelText: 'Enter email',
),
validator: _profileController.emailValidator,
),
),
Center(
child: GestureDetector(
onTap: () {
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return SafeArea(
child: Wrap(
children: <Widget>[
ListTile(
leading: const Icon(Icons.photo_library),
title: const Text('Photo Library'),
onTap: () {
_profileController.avatarFromGallery();
Navigator.of(context).pop();
}),
ListTile(
leading: const Icon(Icons.photo_camera),
title: const Text('Camera'),
onTap: () {
_profileController.avatarFromCamera();
Navigator.of(context).pop();
},
),
],
),
);
}
);
},
child: CircleAvatar(
radius: 55,
backgroundColor: Colors.pink,
child: Obx(() =>(_profileController.avatarPath.value != null)
? ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.file(
File(_profileController.avatarPath.value!),
width: 100,
height: 100,
fit: BoxFit.fitHeight
),
)
: Container(
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(50)),
width: 100,
height: 100,
child: Icon(
Icons.camera_alt,
color: Colors.grey[800],
),
),
),
),
)),
Container(
margin: const EdgeInsets.all(10),
width: double.infinity,
child: MaterialButton(
color: Colors.blue,
onPressed: () => _profileController.save(profileFormKey),
child: const Text(
'Submit',
style: TextStyle(color: Colors.white),
),
),
),
],
),
),
),
);
}
}
as you can see, I have Obx and my avatarPath reactive, and I'm running update everywhere I changing it, but it's not udpated. I also tried to use empty string as initial value of imagePath like this Rx<String> avatarPath = ''.obs; and it's not working. What I'm doing wrong??? Thank you in advice!!!
There are two things to revise for it. Firstly, change avatarPath = localAvatar.path.obs; with avatarPath.value = localAvatar.path;. Because localAvatar.path.obs create the new observable and changes will not be reflected to previous observers.
Secondly, create a new stateless widget having the widget tree of bottom sheet's builder like
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return CustomBottomView();
}
);
Then inside the CustomBottomView copy your bottom sheet widget tree.
class CustomBottomView extends GetView<ProfileController> {
return YourBottomSheetWidgetTreeHere();
}
Dont worry about ProfileController here. You have already put it in DI in previous route. First follow the first step if you still face the problem second step will definitely resolve it.