I'm using Flutter to build a Chat Application.
In my message bubbles, I display the message text, the date, and an icon to show if the message was read or not. The text of the message is poorly displayed. It goes to the next line after just one or two words instead of filling the full width of the bubble.
Container(
padding: EdgeInsets.symmetric(
horizontal: 15.0, vertical: 10.0),
width: MediaQuery.of(context).size.width * 0.65,
margin: EdgeInsets.only(top: 8.0, bottom: 8.0, left: 80.0, right: 10),
decoration: BoxDecoration(
color: primaryColor.withOpacity(.1),
borderRadius: BorderRadius.circular(20)),
child: Column(
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: Container(
child: Text(
documentSnapshot.data['text'],
style: TextStyle(
color: Colors.black87,
fontSize: 16.0,
fontWeight: FontWeight.w600,
),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
documentSnapshot.data["time"] != null
? DateFormat.MMMd().add_jm()
.format(documentSnapshot.data["time"].toDate())
.toString()
: "",
style: TextStyle(
color: secondryColor,
fontSize: 13.0,
fontWeight: FontWeight.w600,
),
),
SizedBox(width: 5),
documentSnapshot.data['isRead'] == false
? Icon(
Icons.done,
color: secondryColor,
size: 15,
)
: Icon(
Icons.done_all,
color: primaryColor,
size: 15,
)
],
),
Short answer
Currently, you are using a Row Widget to display the message text as the first child and then the date and read icon as the second child:
Instead, you should use a Column Widget.
Full solution
After going back and forth with Julien
1. Domain Layer
class ChatEntry {
final String text;
final DateTime date;
final bool read;
final bool sent;
ChatEntry({
this.text,
this.date,
this.read,
this.sent,
});
}
2. Chat Bubble
class Bubble extends StatelessWidget {
final ChatEntry entry;
const Bubble({Key key, this.entry}) : super(key: key);
#override
Widget build(BuildContext context) {
return Align(
alignment: entry.sent ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
padding: kBubblePadding,
decoration: BoxDecoration(
color: (entry.sent ? kSentColor : kReceivedColor)
.withOpacity(entry.read ? kReadOpacity : 1),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(kBorderRadius),
topRight: Radius.circular(kBorderRadius),
bottomRight: Radius.circular(entry.sent ? 0.0 : kBorderRadius),
bottomLeft: Radius.circular(entry.sent ? kBorderRadius : 0.0),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
entry.sent ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
Text(entry.text, style: kBubbleTextStyle),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
DateFormat('MMMd – kk:mm').format(entry.date),
style: TextStyle(fontSize: kBubbleMetaFontSize),
),
if (entry.read) ...[
const SizedBox(width: 5),
Icon(Icons.done, size: kBubbleMetaFontSize)
]
],
),
],
),
),
);
}
}
3. Chat Conversation
class Conversation extends StatelessWidget {
final List<ChatEntry> entries;
const Conversation({Key key, this.entries}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: entries
.map(
(entry) => Padding(
padding: const EdgeInsets.all(8.0),
child: Bubble(entry: entry),
),
)
.toList(),
);
}
}
4. Application
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Chat Demo',
home: Scaffold(
body: SingleChildScrollView(
child: Conversation(entries: getChatEntries()),
),
),
),
);
}
Full Source Code for easy copy-paste
Together with random data generation.
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:faker/faker.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Chat Demo',
home: Scaffold(
body: SingleChildScrollView(
child: Conversation(entries: getChatEntries()),
),
),
),
);
}
class Conversation extends StatelessWidget {
final List<ChatEntry> entries;
const Conversation({Key key, this.entries}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: entries
.map(
(entry) => Padding(
padding: const EdgeInsets.all(8.0),
child: Bubble(entry: entry),
),
)
.toList(),
);
}
}
class Bubble extends StatelessWidget {
final ChatEntry entry;
const Bubble({Key key, this.entry}) : super(key: key);
#override
Widget build(BuildContext context) {
return Align(
alignment: entry.sent ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
padding: kBubblePadding,
decoration: BoxDecoration(
color: (entry.sent ? kSentColor : kReceivedColor)
.withOpacity(entry.read ? kReadOpacity : 1),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(kBorderRadius),
topRight: Radius.circular(kBorderRadius),
bottomRight: Radius.circular(entry.sent ? 0.0 : kBorderRadius),
bottomLeft: Radius.circular(entry.sent ? kBorderRadius : 0.0),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
entry.sent ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
Text(entry.text, style: kBubbleTextStyle),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
DateFormat('MMMd – kk:mm').format(entry.date),
style: TextStyle(fontSize: kBubbleMetaFontSize),
),
if (entry.read) ...[
const SizedBox(width: 5),
Icon(Icons.done, size: kBubbleMetaFontSize)
]
],
),
],
),
),
);
}
}
// DOMAIN
class ChatEntry {
final String text;
final DateTime date;
final bool read;
final bool sent;
ChatEntry({
this.text,
this.date,
this.read,
this.sent,
});
}
// CONFIG
const kSentColor = Color(0xff03bd85);
const kReceivedColor = Color(0xff0251d6);
const kReadOpacity = .3;
const kBorderRadius = 15.0;
const kBubblePadding = const EdgeInsets.symmetric(
horizontal: 15.0,
vertical: 10.0,
);
const kBubbleTextStyle = const TextStyle(
color: Colors.black87,
fontSize: 16.0,
fontWeight: FontWeight.w600,
);
const kBubbleMetaFontSize = 11.0;
// RANDOM DATA
final Random random = Random.secure();
final faker = new Faker();
List<ChatEntry> getChatEntries() {
final nbMessages = random.nextInt(17) + 3;
final lastRead = random.nextInt(nbMessages);
DateTime date = DateTime.now();
return List.generate(
nbMessages,
(index) {
date = date.subtract(Duration(minutes: random.nextInt(30)));
return ChatEntry(
text: faker.lorem
.words(2 + random.nextInt(random.nextBool() ? 3 : 15))
.join(' '),
date: date,
read: index >= lastRead,
sent: random.nextBool(),
);
},
).reversed.toList();
}
Related
i am trying to update the state of the animated switcher in the middle area. i am trying to do this using a setstate in the lower area. but it does not work.
the first thing i did is to create a variable with a boolean data type in the home class.
then i passed the variable to both the middle area and the lower area
the idea was that if the same variable is passed to the class whose state i am trying to update, and the class with the set state, it would work. but it seems i am wrong. i would appreciate some assistance.
the boolean variable i am trying to make work is the _rep variable
This is the Home widget
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
late AnimationController _animationController;
late AnimationController _controller;
late Animation<Offset> _animation;
late Animation<Offset> _anime;
bool _rep = true;
#override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 2)
);
_animation = Tween<Offset>(
begin:Offset (0.0, 0.0),
end: Offset (0.0,3.0),
).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.easeIn));
_anime = Tween<Offset>(
begin:Offset (0.0, 0.0),
end: Offset (0.0,-0.55),
).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.easeIn));
super.initState();
}
#override
void dispose() {
_animationController.dispose();
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
scrollDirection: Axis.vertical,
physics: const NeverScrollableScrollPhysics(),
child: Padding(
padding: EdgeInsets.only(top: 3.h),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TopIcon(icons: Icons.arrow_back, color:Colors.grey.shade300 ,),
SizedBox(
height: 13.h,
width: 13.w,
child: Image.asset('assets/images/download.png')
),
TopIcon(icons: Icons.shopping_bag_outlined, color: Colors.grey.shade300,),
],
),
SizedBox(
height: 3.h,
),
Text('Frappuccino',
style: TextStyle(
fontSize: 27.sp,
fontWeight: FontWeight.bold
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('White Chocolate',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.grey.shade400
),
),
),
MiddleArea(
controller: _animationController,
animation: _animation,
rep: _rep,
),
LowerArea(controller: _animationController, anime: _anime, rep = _rep),
],
),
),
),
);
}
}
This is the middle area
class MiddleArea extends StatefulWidget {
MiddleArea({Key? key, required this.controller, required this.animation, required this.rep}) : super(key: key);
AnimationController controller;
Animation<Offset> animation;
final bool rep;
#override
State<MiddleArea> createState() => _MiddleAreaState();
}
class _MiddleAreaState extends State<MiddleArea> {
bool _flag = true;
bool _favourite = true;
#override
Widget build(BuildContext context) {
print(widget.rep);
return SizedBox(
height: 52.h,
child: Stack(
children: [
Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 135.0),
child: Text('STARBUCKS',
style: TextStyle(
fontFamily: 'Typette',
color: Colors.brown.shade200,
fontSize: 30.sp,
fontWeight: FontWeight.w400
),
),
),
Text('STARBUCKS',
style: TextStyle(
fontFamily: 'Typette',
color: Colors.brown.shade100,
fontSize: 30.sp,
fontWeight: FontWeight.w400
),
),
Text('STARBUCKS',
style: TextStyle(
fontFamily: 'Typette',
color: Colors.brown.shade50,
fontSize: 30.sp,
fontWeight: FontWeight.w400
),
),
],
),
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: const [
SizeAndFave(text: 'Preference'),
SizeAndFave(text: 'Fave!')
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: (){
setState(() {
_flag = !_flag;
});
},
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
transitionBuilder: (Widget child, Animation<double> animation){
return FadeTransition(opacity: animation, child: child,);
},
child: widget.rep == true?Padding(
padding: const EdgeInsets.all(14.0),
key: const Key('1'),
child: Container(
height: 40,
width: 40,
decoration: BoxDecoration(
border: Border.all(
color: Colors.brown.shade300,
width: 3
),
borderRadius: BorderRadius.circular(10)
),
child: const Center(
child: Icon(
Icons.coffee_outlined,
size: 20,
),
)
),
):null,
)
),
GestureDetector(
onTap: (){
setState(() {
_favourite = !_favourite;
});
},
child: _favourite? TopIcon(icons: Icons.favorite_border, color: Colors.brown.shade300)
:TopIcon(
icons: Icons.favorite, color: Colors.brown.shade300)
)
],
)
],
),
AnimatedSwitcher(
duration: Duration(seconds: 1),
transitionBuilder: (Widget child, Animation<double> animation) {
return FadeTransition( opacity: animation,
child: child);
},
child: _flag == true ? Center(
key: const Key('1'),
child: SlideTransition(
position: widget.animation,
child: SizedBox(
height: 80.h,
width: 80.w,
child: Image.asset('assets/images/starcup.png'),
),
),
):Center(
key: const Key('2'),
child: SlideTransition(
position: widget.animation,
child: SizedBox(
height: 80.h,
width: 80.w,
child: Image.asset('assets/images/greeen.png'),
),
),
),
),
Positioned(
child:
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(top: 30.h),
child: TopIcon(
icons: Icons.car_crash_outlined, color: Colors.brown.shade300),
),
)),
const Positioned(
child:
Align(
alignment: Alignment.bottomLeft,
child: Padding(
padding: EdgeInsets.only(top: 330.0, left: 14),
child: Text('\$ 5.99',
style: TextStyle(
fontSize: 27,
fontWeight: FontWeight.bold
),
),
),
))
],
),
);
}
}
and lastly, the lower area
class LowerArea extends StatefulWidget {
final AnimationController controller;
final Animation<Offset> anime;
bool rep;
LowerArea({Key? key, required this.controller, required this.anime, required this.rep}) : super(key: key);
#override
State<LowerArea> createState() => _LowerAreaState();
}
class _LowerAreaState extends State<LowerArea> {
bool _bigger = true;
bool _fade = true;
void move(){
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: EdgeInsets.all(1.h),
child: const Text('Tall Frappuccino',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
),
Padding(
padding: EdgeInsets.only(right: 5.h),
child: const Text('Swipe Down',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
),
Padding(
padding: EdgeInsets.all(2.h),
child: const Text('Pickup',
style: TextStyle(
fontWeight: FontWeight.w500
),
),
)
],
),
),
SlideTransition(
position: widget.anime,
child: AnimatedContainer(
// height: 11.h,
width: _bigger ? 35.h: 80.h,
duration: const Duration(milliseconds: 500),
child: Stack(
fit: StackFit.loose,
children: [
Center(child: Image.asset('assets/images/baggie.png')),
Center(
child: Padding(
padding: EdgeInsets.only(bottom: 4.h),
child: GestureDetector(
onTap: (){
widget.controller.forward();
setState(() {
_bigger = !_bigger;
_fade = !_fade;
widget.rep = !widget.rep;
print('this is fade $_fade ');
});
},
child: AnimatedSwitcher(
duration: Duration(milliseconds: 300),
transitionBuilder: (Widget child, Animation<double> animation){
return FadeTransition(opacity: animation, child: child,);
},
child: _fade? Container(
key: Key('1'),
height: 8.h,
width: 7.w,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(15)
),
child: Column(
children: [
Padding(
padding: EdgeInsets.all(0.3.h),
child: Icon(
Icons.lock_outline,
color: Colors.white54,
size: 2.5.h,
),
),
Icon(
Icons.arrow_drop_down,
color: Colors.white12,
size: 3.h,
),
],
),
):null,
),
),
),
)
],
),
),
)
],
);
}
use provider pacakges https://pub.dev/packages/provider
Create a class that inherits the functions of ChangeNotifyer to create a flag to control and create a setter.
provider class
class StateController extends ChangeNotifier{
bool _req = false;
bool get req => _req; //getter
setReqValue(){
_req = !_req;
notifyListener();
}
}
Declare the provider class in the main function. You can change the location of the declaration according to Wiget tree, but first declare it in main
Change main.dart
void main(){
runApp(
Multiprovider(
providers: [
ChangeNotifierProvider(create: (_) => StateController()),
],
child: HomePage(),
)
);
}
The UI is updated by notifyListener().
change UI
child: context.watch<StateController>().req == true ? Padding(
padding: const EdgeInsets.all(14.0),
key: const Key('1'),
child: Container(
height: 40,
width: 40,
decoration: BoxDecoration(
border: Border.all(
color: Colors.brown.shade300,
width: 3
),
borderRadius: BorderRadius.circular(10)
),
child: const Center(
child: Icon(
Icons.coffee_outlined,
size: 20,
),
)
),
):null,
Change State
onTap: (){
widget.controller.forward();
setState(() {
_bigger = !_bigger;
_fade = !_fade;
context.read<StateController>().setReqValue();
print('this is fade $_fade ');
});
},
I am too lazy to try understand your code. But if you want to update state of the page after you pop from Navigation to it.
In page you want to update
Navigation.push(context, /* Page that will change something */)
// Future will trigger then you return to this page.
.then((_) => setState(() {}))
I am developing an app with flutter and I am getting an error about height.
I have a listview.separated and I have a SingleChildScrollView. I am getting flex error.
This is my file to align widgets inside Scaffold and SafeArea. I have a file for runApp but it is not important for the question.
class ProfilePageC extends StatelessWidget {
const ProfilePageC({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child: Column(
children: [
AboutTopBarW(), // It has not listview
ImageFieldW(), // It has no listview
PercentsW(), //It has no listview
HomeArticleList2W() //It has a listview.separated
],
),
),
),
);
}
}
this is my file for listview.separated widget.
class HomeArticleList2W extends ConsumerWidget {
const HomeArticleList2W({Key? key}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
final futureCatFacts = ref.watch(multiFutureArticleProvider);
return Expanded(
child: futureCatFacts.when(
loading: () => const ShimmerHomeW(),
error: (err, stack) => Text('Error: $err'),
data: (data) {
final decodedData = json.decode(data.body);
return ListView.separated(
separatorBuilder: (BuildContext context, int index) {
if (index % 3 == 0) {
return const Divider();
}
return const Divider();
},
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: decodedData.length ,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.only(
left: 20,
right: 20,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.network(
decodedData[index]['largeImage'].toString(),
fit: BoxFit.cover,
height: 70,
width: 70,
)),
const SizedBox(
width: 10,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
decodedData[index]['title'],
style: const TextStyle(
fontSize: 15, fontWeight: FontWeight.bold),
),
const SizedBox(
height: 5,
),
Row(
children: [
const Icon(
Icons.av_timer_sharp,
size: 20,
),
Text(decodedData[index]['date']),
],
),
],
),
),
const SizedBox(
width: 5,
),
const Icon(Icons.bookmark_border),
],
),
);
},
);
},
),
);
}
}
There is no error when I erase HomeArticleList2W() from the SingleChildScrollView. Therefore I thnink the error consist of the listview.separated.
How to solve this problem.
Remove the expanded widget and add mainAxisSize min
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
decodedData[index]['title'],
style: const TextStyle(
fontSize: 15, fontWeight: FontWeight.bold),
),
const SizedBox(
height: 5,
),
Row(
children: [
const Icon(
Icons.av_timer_sharp,
size: 20,
),
Text(decodedData[index]['date']),
],
),
],
),
I'm new to Flutter and I just start coding the UI of a chat app so I figure out that my widget doesn't take all the space of the bottom navigation bar like the photo shows : shows
This is my camera widget code :
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:image_picker/image_picker.dart';
class Camera extends StatefulWidget {
#override
_CameraState createState() => _CameraState();
}
class _CameraState extends State<Camera> {
File _image;
final imagePicker = ImagePicker();
Future getImage() async {
final image = await imagePicker.getImage(source: ImageSource.camera);
setState(() {
_image = File(image.path);
});
}
#override
Widget build(BuildContext context) {
return SizedBox(
height: 100,
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
width: 80.0,
height: 80.0,
child: FloatingActionButton(
backgroundColor: Colors.white,
onPressed: getImage,
child: Icon(
Icons.camera_alt_rounded,
color: Colors.lightBlue[600],
size: 35.0,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0))),
),
),
],
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.lightBlue[50],
boxShadow: [
BoxShadow(color: Colors.grey[100], spreadRadius: 3),
],
),
height: 50,
),
);
}
}
and this is the chat screen code :
import 'dart:io';
import 'package:mychat/services/auth.dart';
import 'package:flutter/material.dart';
import 'package:mychat/chat/chat.dart';
import 'package:mychat/widgets/Med_form.dart';
import 'package:mychat/widgets/bottomButtons.dart';
import 'package:mychat/widgets/camera.dart';
import 'package:mychat/widgets/incrementAndDecrement.dart';
import 'package:mychat/widgets/yesOrno.dart';
class ChatScreen extends StatefulWidget {
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
File _image;
final AuthService _auth = AuthService();
List<String> messages = [
'fizikefgzerpgr',
'fioezhfejzifojef',
'fvfzerfergnyolnkyokjy',
'rgop^l^lmf^prlgprgprp'
];
List<String> responses = [
'fizikefgzerpgr',
'fezfzefzefezff',
'ofpkoepzkfopkzef',
'fjeziofjiozejfozejf'
];
//final AuthService _auth = AuthService();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(90),
child: AppBar(
title: new Text(
"لاباس ⸮",
style: TextStyle(
color: Colors.black,
fontSize: 30.0,
fontWeight: FontWeight.bold),
),
centerTitle: true,
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomRight,
colors: [Colors.blueGrey[300], Colors.grey[50]])),
),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.arrow_forward_ios_outlined,
color: Colors.black,
),
onPressed: () async {
await _auth.signOut();
})
],
elevation: 0.0,
),
),
body: Chat_page(messages: messages, responses: responses),
bottomNavigationBar: Camera(),
),
));
}
}
and this is the code of the messages:
import 'package:bubble/bubble.dart';
import 'package:flutter/material.dart';
class Chat_page extends StatefulWidget {
List<String> messages;
List<String> responses;
Chat_page({this.messages, this.responses});
#override
_Chat_pageState createState() => _Chat_pageState();
}
class _Chat_pageState extends State<Chat_page> {
String message;
int data;
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
/* Container(
padding: EdgeInsets.only(top: 15, bottom: 10),
child: Text(
"Today, 10",
style: TextStyle(fontSize: 20, color: Colors.white),
),
), */
Flexible(
child: widget.messages.length > 0
? ListView.builder(
reverse: true,
shrinkWrap: true,
itemCount: widget.messages.length,
itemBuilder: (context, index) => Column(
children: [
widget.messages.length > 0
? chat(widget.messages[index].toString(), 1)
: Container(),
widget.responses.length > 0
? chat(widget.responses[index].toString(), 0)
: Container(),
],
))
: Container(),
),
SizedBox(
height: 20,
),
SizedBox(
height: 15.0,
)
],
));
}
}
Widget chat(String message, int data) {
return Container(
padding: EdgeInsets.only(left: 20, right: 20),
child: Row(
mainAxisAlignment:
data == 1 ? MainAxisAlignment.end : MainAxisAlignment.start,
children: [
data == 0
? Container(
height: 60,
width: 60,
child: CircleAvatar(
child: Icon(Icons.account_circle),
),
)
: Container(),
Padding(
padding: EdgeInsets.all(10.0),
child: Bubble(
radius: Radius.circular(15.0),
color: data == 0 ? Color(0xFFf7ede2) : Color(0xFFf7ede2),
elevation: 0.0,
child: Padding(
padding: EdgeInsets.all(2.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(
width: 10.0,
),
Flexible(
child: Container(
constraints: BoxConstraints(maxWidth: 200),
child: Text(
message,
style: TextStyle(
color: Colors.black, fontWeight: FontWeight.bold),
),
))
],
),
)),
),
data == 1
? Container(
height: 60,
width: 60,
child: CircleAvatar(
child: Icon(Icons.account_circle),
),
)
: Container(),
],
),
);
}
so how can I fix this the make my camera widget take the full space
try to change
SizedBox(
height: 100,
child: Container(
to
Container(
height: 100,
width:double.infinty,
I want to add text field in flutter and when user add value quatity value should be updated you can see in that below item image there is quantity I want to add a text field against it so user can edit quantity as much as he wanted to add you can ask anything you want to ask
here is the quantity selection
class QuantitySelection extends StatelessWidget {
final int limitSelectQuantity;
final int value;
final double width;
final double height;
final Function onChanged;
final Color color;
QuantitySelection(
{#required this.value,
this.width = 40.0,
this.height = 42.0,
this.limitSelectQuantity = 100,
#required this.color,
this.onChanged});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
if (onChanged != null) {
showOptions(context);
}
},
child: Container(
decoration: BoxDecoration(
border: Border.all(width: 1.0, color: kGrey200),
borderRadius: BorderRadius.circular(3),
),
height: height,
width: width,
child: Padding(
padding: EdgeInsets.symmetric(
vertical: 2.0, horizontal: (onChanged != null) ? 5.0 : 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: Center(
child: Text(
value.toString(),
style: TextStyle(fontSize: 14, color: color),
),
),
),
if (onChanged != null)
const SizedBox(
width: 5.0,
),
if (onChanged != null)
Icon(Icons.keyboard_arrow_down,
size: 14, color: Theme.of(context).accentColor)
],
),
),
),
);
}
void showOptions(context) {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
for (int option = 1;
option <= limitSelectQuantity;
option++)
ListTile(
onTap: () {
onChanged(option);
Navigator.pop(context);
},
title: Text(
option.toString(),
textAlign: TextAlign.center,
)),
],
),
),
),
Container(
height: 1,
decoration: BoxDecoration(color: kGrey200),
),
ListTile(
title: Text(
S.of(context).selectTheQuantity,
textAlign: TextAlign.center,
),
),
],
);
});
}
}
here is shopping cart
class ShoppingCartRow extends StatelessWidget {
ShoppingCartRow(
{#required this.product,
#required this.quantity,
this.onRemove,
this.onChangeQuantity,
this.variation});
final Product product;
final ProductVariation variation;
final int quantity;
final Function onChangeQuantity;
final VoidCallback onRemove;
#override
Widget build(BuildContext context) {
String currency = Provider.of<AppModel>(context).currency;
final currencyRate = Provider.of<AppModel>(context).currencyRate;
final price = Services()
.widget
.getPriceItemInCart(product, variation, currencyRate, currency);
final imageFeature = variation != null && variation.imageFeature != null
? variation.imageFeature
: product.imageFeature;
int maxQuantity = kCartDetail['maxAllowQuantity'] ?? 100;
int totalQuantity = variation != null
? (variation.stockQuantity ?? maxQuantity)
: (product.stockQuantity ?? maxQuantity);
int limitQuantity =
totalQuantity > maxQuantity ? maxQuantity : totalQuantity;
ThemeData theme = Theme.of(context);
return LayoutBuilder(
builder: (context, constraints) {
return Column(children: [
Row(
key: ValueKey(product.id),
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (onRemove != null)
IconButton(
icon: Icon(Icons.remove_circle_outline),
onPressed: onRemove,
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Stack(children: <Widget>[
Container(
width: constraints.maxWidth * 0.25,
height: constraints.maxWidth * 0.3,
child: Tools.image(url: imageFeature)),
Positioned(
bottom: 0,
right: 0,
child: Container(
decoration: BoxDecoration(
border: Border.all(width: 1.0, color: kGrey200),
color: Theme.of(context).backgroundColor,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(2.0)),
),
child: QuantitySelection(
width: 60,
height: 32,
color: Theme.of(context).accentColor,
limitSelectQuantity: limitQuantity,
value: quantity,
onChanged: onChangeQuantity,
),
),
)
]),
SizedBox(width: 16.0),
Expanded(
child: Container(
),
),
Expanded(
child: Container(
height: constraints.maxWidth * 0.3,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: TextStyle(
color: theme.accentColor,
),
maxLines: 4,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 7),
Text(
price,
style: TextStyle(
color: theme.accentColor, fontSize: 14),
),
SizedBox(height: 10),
variation != null
? Services()
.widget
.renderVariantCartItem(variation)
: Container(),
],
),
),
),
],
),
),
),
],
),
SizedBox(height: 10.0),
Divider(color: kGrey200, height: 1),
SizedBox(height: 10.0),
]);
},
);
}
}
I think having an editable number field with plus and minus button would be a nice and user-friendly solution. There's a number of ways to do this with flutter - check out the solutions in this thread for ideas.
I am developing an app with a dropdown. Below is my code. I have removed the UI design code to isolate the dropdown section itself.
class ShoppingCartUIState extends State<ShoppingCartUI> {
final _formKey = GlobalKey<FormState>();
String _checkoutDropdownValue=null;
//**UI design Code Removed**//
_showCheckoutPopup() {
String date=DateTime.now().toString();
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0)), //this right here
child: Container(
height: MediaQuery.of(context).size.height/3,
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Container(
margin: EdgeInsets.all(10),
child: Text(
"What is Your Required Delivery Date?",
style: Theme.of(context).textTheme.subtitle,
),)
],
),
Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.calendar_today),
color: Colors.green,
onPressed: () {
date = "111";
},
),
Text(date)
],
),
Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(top:20, left:10),
child: Text(
"What is your Airport of delivery?",
style: Theme.of(context).textTheme.subtitle,
),)
],
),
Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(top:5, left:10),
child: DropdownButton(
hint: Text(
"Please Select ",
style: TextStyle(
fontSize: 14,
),
),
items: <String>[
'Skinless Boneless, Full Loins',
'brown',
'silver'
].map((data) {
return DropdownMenuItem(
child: new Text(data,
style: Theme.of(context).textTheme.body1),
value: data,
);
}).toList(),
onChanged: (String newValue) {
setState(() {
_checkoutDropdownValue = newValue;
print(newValue);
});
},
value: _checkoutDropdownValue),
)
],
),
],
),
));
}
#override
void initState() {
// TODO: implement initState
super.initState();
}
}
The issue is when I change the dropdown item, the new value never get selected. The previously selected value is always displayed. However since i am using a print when dropdown is done, I can see the item has changed.
How can I solved this issue?
Wrap your Dialog widget with StatefulBuilder to rebuild the dialog.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyPage(), //TODO: Add Scaffold
);
}
}
class MyPage extends StatefulWidget {
#override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
String date = "";
String _checkoutDropdownValue;
_showCheckoutPopup() {
return StatefulBuilder(
builder: (context, setState){
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
), //this r// ight here
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
margin: EdgeInsets.all(10),
alignment: Alignment.centerLeft,
child: Text(
"What is Your Required Delivery Date?",
style: Theme.of(context).textTheme.subtitle,
),
),
Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.calendar_today),
color: Colors.green,
onPressed: () {
setState(() {
date = "111";
});
},
),
Text(date)
],
),
Container(
margin: EdgeInsets.only(top: 20, left: 10),
alignment: Alignment.centerLeft,
child: Text(
"What is your Airport of delivery?",
style: Theme.of(context).textTheme.subtitle,
),
),
Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 5, left: 10),
child: DropdownButton<String>(
hint: Text(
"Please Select ",
style: TextStyle(
fontSize: 14,
),
),
items: <String>[
'Skinless Boneless, Full Loins',
'brown',
'silver'
].map((data) {
return DropdownMenuItem(
child: new Text(data,
style: Theme.of(context).textTheme.body1),
value: data,
);
}).toList(),
onChanged: (String newValue) {
setState(() {
_checkoutDropdownValue = newValue;
});
},
value: _checkoutDropdownValue,
),
)
],
),
],
),
),
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
child: Text("Click Here"),
onPressed: () {
showDialog(
context: context,
builder: (context) => _showCheckoutPopup(),
);
},
),
),
);
}
}
can you change your onPressed to print(_checkoutDropdownValue); instead of print(newValue); that way we can see if there is a problem with the assignment