I am working on a chat application using flutter. In that, I have to create a chat screen containing messages. I want each message container to take only the required space. For example: Short message, short container width & Long message, long container. Currently, it's taking full space of the Column container no matter what's the length of the message.
As a side note, I have tried wrapping the message container inside flexible but it didn't work at all.
Here's my code:
import 'package:flutter/material.dart';
import 'package:memomessenger/Services/Chats.dart';
import 'package:memomessenger/Services/Constants.dart';
import 'package:memomessenger/Services/Types/Chat.dart';
import 'package:memomessenger/Services/Types/ChatsActivity.dart';
import 'package:memomessenger/Services/User.dart';
Widget buildMessageWidget(
BuildContext context, Message message, String currentUserId) {
final bool isFromCurrentUser = message.senderId == currentUserId;
return LimitedBox(
maxWidth: MediaQuery.of(context).size.width * .75,
child: Container(
margin: EdgeInsets.symmetric(vertical: 2),
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(
isFromCurrentUser ? 0 : messageBorderRadius,
),
topLeft: Radius.circular(
isFromCurrentUser ? messageBorderRadius : 0,
),
bottomLeft: Radius.circular(messageBorderRadius),
bottomRight: Radius.circular(messageBorderRadius),
),
color: isFromCurrentUser ? themeAccentColor[500] : Colors.cyan,
),
child: Align(
alignment:
isFromCurrentUser ? Alignment.centerRight : Alignment.centerLeft,
child: Text(
message.text,
style: TextStyle(
color: isFromCurrentUser ? Colors.white : Colors.black,
),
),
),
),
);
}
Widget buildChatMessagesUI(BuildContext context, List<Message> messages) {
final String currentUserId = currentUser.value.id;
return SingleChildScrollView(
child: Column(
verticalDirection: VerticalDirection.up,
children: messages.reversed.map((Message message) {
return buildMessageWidget(context, message, currentUserId);
}).toList(),
),
);
}
Widget buildMessageInputUI({#required String chatId}) {
final String currentUserId = currentUser.value.id;
return TextFormField(
decoration: const InputDecoration(
hintText: 'Enter your email',
),
onChanged: (String message) {
sendMessage(chatId, Message(text: message, senderId: currentUserId));
},
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
autofocus: true,
autocorrect: true,
keyboardType: TextInputType.multiline,
);
}
class ChatActivity extends StatelessWidget {
final String chatId;
ChatActivity({#required this.chatId});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: StreamBuilder<Map<String, Chat>>(
stream: chatActivityChatList,
initialData: {},
builder: (BuildContext context,
AsyncSnapshot<Map<String, Chat>> data) {
if (data.hasData && data.data[chatId] != null) {
return buildChatMessagesUI(
context, data.data[chatId].messages);
} else {
return Text("No Data");
}
},
),
),
buildMessageInputUI(chatId: chatId),
],
),
),
),
),
);
}
}
The Align widget sizes itself to its incoming constraints if bounded, and your Container with a decoration is placed around it. This means the decoration will cover the entire width of the column. To fix this, the DecoratedBox (produced by Container) needs to be a descendant of Align instead.
Another problem you may have noticed is that LimitedBox does nothing if the incoming constraint is already bounded. In order to tighten the existing bound, you will need to provide constraints in your Container (or equivalently, a ConstrainedBox widget). FractionallySizedBox might also be worth considering.
Widget buildMessageWidget(
BuildContext context, Message message, String currentUserId) {
final bool isFromCurrentUser = message.senderId == currentUserId;
return Align(
alignment:
isFromCurrentUser ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
margin: EdgeInsets.symmetric(vertical: 2),
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width * .75
),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(
isFromCurrentUser ? 0 : messageBorderRadius,
),
topLeft: Radius.circular(
isFromCurrentUser ? messageBorderRadius : 0,
),
bottomLeft: Radius.circular(messageBorderRadius),
bottomRight: Radius.circular(messageBorderRadius),
),
color: isFromCurrentUser ? themeAccentColor[500] : Colors.cyan,
),
child: Text(
message.text,
style: TextStyle(
color: isFromCurrentUser ? Colors.white : Colors.black,
),
),
),
);
}
Related
I want to implement OTP verification screen without any package.
when i entered the number it should move to next input field
I m using this code in my current project take a refrence this will help
class Otp extends StatefulWidget {
final String? phnNumber;
final String ? code;
String? from;
Otp({Key ?key, this.phnNumber, this.from, this.code}) : super(key:
key);
#override
_OtpState createState() => _OtpState();
}
class _OtpState extends State<Otp> {
double ? height ;
double ? width;
TextEditingController ? contrller1;
TextEditingController ? contrller2;
TextEditingController ? contrller3;
TextEditingController ? contrller4;
SendOtpRequest resend = SendOtpRequest();
SharedPreferences ? prefs;
getSharedPreferences () async
{
prefs = await SharedPreferences.getInstance();
}
String Code = "";
#override
void initState() {
// TODO: implement initState
super.initState();
contrller1 = TextEditingController();
contrller2 = TextEditingController();
contrller3 = TextEditingController();
contrller4 = TextEditingController();
getSharedPreferences();
}
#override
Widget build(BuildContext context) {
height= MediaQuery.of(context).size.height;
width = MediaQuery.of(context).size.height;
final verifyprovider = Provider.of<PostDataProvider>(context);
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
toolbarHeight:height! * 0.07802345,
titleSpacing: 0,
backgroundColor: HexColor("#18263d"),
automaticallyImplyLeading: false,
leading: Padding(
padding: const EdgeInsets.only(left: 8.0,),
child: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Container(
color: Colors.transparent,
child: Image.asset("assets/images/back_ic-1.png")),
),
),
// SizedBox(width: width!*0.001234,),
title:Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
height: height!/15,
width: height!/15,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
width: 2,
color:HexColor("#fc4f00"),
)),
child: Padding(
padding: const EdgeInsets.all(1.0),
child: Container(
height: height!/11,
width: height!/11,
decoration: BoxDecoration(
image: const DecorationImage(
image:
AssetImage("assets/images/home_logo.png"),
fit: BoxFit.fill
),
shape: BoxShape.circle,
border: Border.all(
width: 1,
color:HexColor("#fc4f00"),
)),
),
),
),
SizedBox(width: width! * 0.04234,),
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text("Verification",
style: GoogleFonts.oswald(fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: width! * 0.03345
),),
),
],
) ,
),
body: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 24, horizontal: 32),
child: Column(
children: [
Text("We have send verification code on your mobile number",
style: GoogleFonts.oswald(fontStyle: FontStyle.normal,
fontSize: width!*0.0234,
color: HexColor("#8b8b8b")),
),
SizedBox(height: height!/38,),
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_textFieldOTP(first: true, last: false,controllerr:
contrller1),
_textFieldOTP(first: false, last: false,controllerr:
contrller2),
_textFieldOTP(first: false, last: false,controllerr:
contrller3),
_textFieldOTP(first: false, last: true, controllerr:
contrller4),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap:() {
resend.phoneNumber= widget.phnNumber;
resend.countryCode = widget.code;
verifyprovider.resendOtp(context,
jsonEncode(resend));
},
child: Text("Resend OTP?",
style: GoogleFonts.oswald(fontStyle:
FontStyle.normal,
fontSize: width!*0.0234,
color: HexColor("#fc4f00")),
),
),
),
SizedBox(height: height!/28,),
GestureDetector(
onTap: (){
if(contrller1!.text.isNotEmpty&&
contrller2!.text.isNotEmpty&&contrller3!.
text.isNotEmpty&&contrller4!.text.isNotEmpty){
verifyOtpRequest verify = verifyOtpRequest();
verify.phoneNumber = widget.phnNumber;
verify.otp=
contrller1!.text+contrller2!.
text+contrller3!.text+contrller4!.text;
verifyprovider.otpVerification(context,
jsonEncode(verify), widget.from);
}else{
CommonUtils.showToast(msg: "Please fill all the
fields ");
}
},
child: Container(
height: height!/18,
width: width,
decoration: BoxDecoration(
color: HexColor("#fc4f00"),
borderRadius: BorderRadius.circular(10)
),
child: Center(
child: Text("Verify",style: TextStyle(
color: Colors.white,
fontSize: width!*0.02345
),),
)
),
),
],
),
],
),
),
),
);
}
Widget _textFieldOTP({bool ? first, last,
TextEditingController ?
controllerr}) {
return Container(
height:height!/12 ,
child: AspectRatio(
aspectRatio: 1.0,
child: TextField(
controller: controllerr,
autofocus: true,
onChanged: (value) {
if (value.length == 1 && last == false) {
FocusScope.of(context).nextFocus();
}
if (value.length == 0 && first == false) {
FocusScope.of(context).previousFocus();
}
},
showCursor: false,
readOnly: false,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
keyboardType: TextInputType.number,
maxLength: 1,
decoration: InputDecoration(
counter: Offstage(),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(width: 2, color: Colors.black54),
borderRadius: BorderRadius.circular(12)),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(width: 2, color: Colors.black54),
borderRadius: BorderRadius.circular(12)),
),
),
),
);
}
}
When the length of the input data reaches one, you will have to change the text field focus node.
For Example
If you are in the first field, and you enter a number field one focus should be lost, and field two should be in focus. This can be done, by requestFocus.
This article will of help for you: Flutter Focus
I created a custom FormBuilderTextField with built in validation and am able to build them when called through LisView.Builder. My problem now is that I am unable to access the data within each generated FormBuilderTextField and pass that data to another page. I have a GlobalKey<FormState>() on the page with the ListView.Builder but cant seem to access that data. Any advice?
Custom TextField
TextFieldEntry({
required this.name,
});
final String name;
final myDecorationField = InputDecoration(
labelText: 'textFieldName',
labelStyle: TextStyle(fontSize: 20, color: Colors.pink),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(0),
bottomRight: Radius.circular(0),
),
borderSide: BorderSide(
width: 1,
color: Colors.pink,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(0),
bottomRight: Radius.circular(0),
),
borderSide: BorderSide(
color: Colors.pink,
width: 1,
),
),
);
#override
State<TextFieldEntry> createState() => _TextFieldEntryState();
}
class _TextFieldEntryState extends State<TextFieldEntry> {
Input model = Input();
var _validator;
var _keyboard;
var _myUnit;
Widget build(BuildContext context) {
var _onSaved = (value) {
model.title = value;
};
if (widget.name == 'height') {
final heightValidate = FormBuilderValidators.compose([
FormBuilderValidators.required(context),
FormBuilderValidators.numeric(context),
FormBuilderValidators.max(context, 200),
FormBuilderValidators.min(context, 30)
]);
_validator = heightValidate;
_keyboard = TextInputType.number;
_myUnit = MyUnit();
} else {
final weightValidate = FormBuilderValidators.compose([
FormBuilderValidators.required(context),
FormBuilderValidators.numeric(context),
FormBuilderValidators.max(context, 450),
FormBuilderValidators.min(context, 30)
]);
_validator = weightValidate;
_keyboard = TextInputType.number;
_myUnit = MyUnit(imperial: 'lbs', metric: 'kg');
}
return Padding(
padding: const EdgeInsets.all(2.0),
child: Column(
children: [
Container(
constraints: BoxConstraints(maxWidth: 375, maxHeight: 50),
child: Row(
children: [
Expanded(
child: FormBuilderTextField(
name: widget.name,
decoration: widget.myDecorationField
.copyWith(labelText: widget.name),
onSaved: _onSaved,
validator: _validator,
keyboardType: _keyboard,
),
),
Container(
decoration: BoxDecoration(
color: Colors.blue,
border: Border.all(color: Colors.pink, width: 2)),
child: Row(
children: [
Container(
padding: EdgeInsets.all(5),
child: Center(
child: ValueListenableBuilder<Units>(
valueListenable: _myUnit,
builder: (context, unit, _) => AutoSizeText(
_myUnit.unitType,
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w500),
)),
),
),
Builder(
builder: (context) {
if (widget.name == 'height' ||
widget.name == 'weight' ||
{
return Container(
constraints:
BoxConstraints(maxHeight: 50, maxWidth: 60),
child: TextButton(
onPressed: () {
Units unit = _myUnit.unit;
_myUnit.unit = unit == Units.unit1
? Units.unit2
: Units.unit1;
},
child: Center(
child: Icon(
Icons.loop,
size: 30,
color: Colors.white,
),
),
),
);
} else {
return Text('');
}
},
),
],
),
),
],
),
),
SizedBox(
height: 12,
)
],
),
);
}
}
ListView.Builder Page
var title;
Input({this.title});
}
class BMI extends StatefulWidget {
static const String id = 'BMI';
#override
State<BMI> createState() => _BMIState();
}
class _BMIState extends State<BMI> {
final _formKey = GlobalKey<FormState>();
List<Input> _bmiInputs = [
Input(title: 'height'),
Input(title: 'weight'),
Input(title: 'gender'),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: 'BMI',
),
drawer: MainDrawer(),
backgroundColor: Theme.of(context).colorScheme.secondary,
body: SingleChildScrollView(
child: Column(children: [
Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
constraints: BoxConstraints(maxHeight: 500, maxWidth: 375),
child: ListView.builder(
itemCount: _bmiInputs.length,
itemBuilder: (BuildContext inp, index) {
return FittedBox(
child: TextFieldEntry(
name: _bmiInputs[index].title,
),
);
},
),
),
ElevatedButton(
child: Text('Calculate'),
onPressed: () {
_formKey.currentState?.save();
if (_formKey.currentState!.validate()) {}
print('Calculate');
// need to access Data here to pass to the calculator
// BMICalc bmiCalc;
// bmiCalc = BMICalc(
// height: ______,
// weight: ______,
// gender: _______,
// );
Navigator.push(context,
MaterialPageRoute(builder: (context) => ResultsScreen(bmi: bmiCalc.calculateBMI(),)));
})
],
),
),
])));
}
}
you use the onSaved as a parameter, you can get Data back to the first Widget
in the FormBuilderTextField it will be at the onSaved
ListView.Builder
return FittedBox(
child: TextFieldEntry(
name: _bmiInputs[index].title,
onSaved: (value) {
_bmiInputs[index].title = value;
}),
);
TextFieldEntry
typedef OnSaved(value);
TextFieldEntry({
required this.name, required this.onSaved
});
final String name;
final OnSaved onSaved;
...
...
FormBuilderTextField(
name: widget.name,
decoration: widget.myDecorationField
.copyWith(labelText: widget.name),
onSaved: widget.onSaved,
validator: _validator,
keyboardType: _keyboard,
),
This about a chat application made using firestore and I cant scroll this widget , when the screen is filled it just stop showing the new items, like its going user the screen,it is basically the chat feature of the app , a chat should scroll,there aren't any syntax errors, I ma new to flutter, please help
class ChatScreen extends StatefulWidget {
String chatRoomId;
#override
ChatScreen(this.chatRoomId);
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
DatabaseMethods databaseMethods = new DatabaseMethods();
TextEditingController messageController = new
TextEditingController();
Stream chatmessageStream;
Widget chatMessageList() {
return StreamBuilder(
stream: chatmessageStream,
builder: (context, snapshot) {
return snapshot.hasData
? ListView.builder(
scrollDirection: Axis.vertical,
itemCount:
snapshot.hasData ? snapshot.data.documents.length : 1,
itemBuilder: (context, index) {
return MessageTile(
snapshot.data.documents[index].get("message"),
snapshot.data.documents[index].get("sendBy") ==
Constants.myName);
})
: Container(width: 100, height: 100, child: Text(''));
});
}
sendMessage() {
if (messageController.text.isNotEmpty) {
Map<String, dynamic> messageMap = {
"message": messageController.text,
"sendBy": Constants.myName,
"time": DateTime.now().millisecondsSinceEpoch,
};
databaseMethods.addConversationMessages(widget.chatRoomId, messageMap);
messageController.text = "";
}
}
void initState() {
databaseMethods.getConversationMessages(widget.chatRoomId)
.then((value) {
setState(() {
chatmessageStream = value;
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: User(widget.chatRoomId
.toString()
.replaceAll("_", "")
.replaceAll(Constants.myName, "")),
),
body: Container(
child: Stack(
children: [
chatMessageList(),
Container(
alignment: Alignment.bottomCenter,
decoration:
BoxDecoration(borderRadius: BorderRadius.circular(30)),
child: Container(
color: Color(0xff1F1F1F),
padding: EdgeInsets.all(3),
child: Row(
children: [
Expanded(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 22),
child: TextField(
cursorColor: Colors.redAccent,
style: TextStyle(color: Colors.white, fontSize: 18),
controller: messageController,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: BorderSide.none,
//borderRadius: BorderRadius.circular(100),
),
hintText: "Type a message ",
// filled: true,
fillColor: Colors.grey[800],
hintStyle: TextStyle(color: Colors.white54),
border: InputBorder.none),
),
),
),
GestureDetector(
onTap: () {
sendMessage();
},
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
gradient: LinearGradient(
colors: [Colors.red[400], Colors.red[400]])),
padding: EdgeInsets.all(12),
child: Image.asset(
"assets/images/send.jpeg",
),
),
)
],
),
),
),
],
),
),
);
}
}
class User extends StatelessWidget {
final String userName;
User(this.userName);
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Row(
children: [
Text(
userName,
style: textFieldTextStyle(),
)
],
),
);
}
}
class MessageTile extends StatelessWidget {
final String message;
final bool isSendByMe;
MessageTile(this.message, this.isSendByMe);
#override
Widget build(BuildContext context) {
return Container(
padding:
EdgeInsets.only(left: isSendByMe ? 0 : 4, right: isSendByMe ? 14 : 0),
margin: EdgeInsets.symmetric(vertical: 8),
width: MediaQuery.of(context).size.width,
alignment: isSendByMe ? Alignment.centerRight :
Alignment.centerLeft,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 16),
decoration: BoxDecoration(
borderRadius: isSendByMe
? BorderRadius.only(
topLeft: Radius.circular(23),
topRight: Radius.circular(23),
bottomLeft: Radius.circular(23),
)
: BorderRadius.only(
topLeft: Radius.circular(23),
topRight: Radius.circular(23),
bottomRight: Radius.circular(23),
),
color: isSendByMe ? Colors.redAccent : Colors.grey[850]),
child: Text(
message,
style: textFieldTextStyle(),
),
),
);
}
}
please through the code.
there is no need to AlwaysScrollableScrollPhysics just remove this,
set your container width height for blank condition.
SingleChildScrollView(
child:Stack( children: [
chatMessageList(),
Container(..............),
],
),
),
return StreamBuilder(
stream: chatmessageStream,
builder: (context, snapshot) {
return snapshot.hasData
? ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
itemCount:
snapshot.hasData ? snapshot.data.documents.length : 1,
itemBuilder: (context, index) {
return MessageTile(
snapshot.data.documents[index].get("message"),
snapshot.data.documents[index].get("sendBy") ==
Constants.myName);
})
: Container(
width: 100,
height: 100,
child: Text('')
);
});
}
I'm really frustrated on this one.
I have 3 functions that calls showModalBottomSheet in each function.
2 Modals that are Stateful and 1 Stateless.
I'm having problem with the other Stateful Modal.
They are both called using InkWell's onTap function. and both are also not occupying the whole screen therefore shows the main page.
MY STATEFUL MODAL THAT DOESN'T TRIGGER THE FUTURE/ DOESN'T REFRESH THE PAGE:
void showCommentsModal(int id) {
showModalBottomSheet(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
backgroundColor: Color(colorBackground),
isScrollControlled: true,
context: context,
builder: (BuildContext context) {
return Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
color: Color(colorBackground),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20), topRight: Radius.circular(20))),
height: MediaQuery.of(context).size.height / 1.5,
width: MediaQuery.of(context).size.width,
child: ViewCommentModal(
postID: id,
userList: userList,
),
);
},
);
}
THE OTHER STATEFUL MODAL THAT TRIGGERS THE FUTURE/ REFRESHES THE LIST:
addCommentModal(int i) {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: AddCommentModal(
onPost: (String text) {
APIServices.commentPost(context, i.toString(), text);
Navigator.pop(context);
},
),
);
},
);
}
what makes the addCommentModal trigger the future? please help.
EDIT:
I've caught the suspect!
here is my AddCommentModal code:
class AddCommentModal extends StatefulWidget {
final ValueChanged<String> onPost;
AddCommentModal({#required this.onPost});
#override
_AddCommentModalState createState() => _AddCommentModalState();
}
class _AddCommentModalState extends State<AddCommentModal> {
final commentController = TextEditingController();
String defaultProfilePhoto = "";
#override
void initState() {
defaultProfilePhoto = Constants.userFirstName[0].toUpperCase();
super.initState();
}
#override
void dispose() {
commentController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(10),
child: Row(
children: <Widget>[
Container(
width: 50,
height: 50,
child: ClipRRect(
borderRadius: new BorderRadius.circular(50),
child: Constants.userProfilePhoto == null
? Container(
color: Color(colorPrimary),
alignment: Alignment.center,
child: Text(
defaultProfilePhoto,
style: TextStyle(
color: Color(colorText), fontSize: 20),
),
)
: Image.network(
APIServices.httpDomain + Constants.userProfilePhoto,
fit: BoxFit.cover,
)),
),
Expanded(
child: Container(
margin: EdgeInsets.only(
left: 10,
),
child: TextFormField(
controller: commentController,
decoration: new InputDecoration(
suffixIcon: IconButton(
onPressed: () => widget.onPost(commentController.text),
icon: Icon(
FontAwesomeIcons.paperPlane,
size: 15,
color: Theme.of(context).primaryColor,
)),
contentPadding: EdgeInsets.all(10),
hintText: "Add a comment ...",
fillColor: Colors.white,
border: new OutlineInputBorder(
borderRadius: new BorderRadius.circular(20.0),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(20)),
borderSide:
BorderSide(width: 1, color: Color(0xFFff8b50)))),
keyboardType: TextInputType.text,
style: new TextStyle(fontFamily: "Poppins", fontSize: 15),
),
))
],
));
}
}
The issue that causes the page to refresh is because of the SoftKeyboard that pops up whenever the TextFormField is focused.
When I show the the modal, it runs smoothly. but when I start to tap the field, the page refreshes as if the future is once again called.
I am learning flutter I want a particular widget to be reused with different images at run time.
How to attain this is giving me difficulties I would like to know how to get this.
I am writing the peice of code kindly suggest what is a correct way
scaffold: CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
trailing: Image.asset('assets/Menu_Button.png'),
automaticallyImplyLeading: false,
backgroundColor: Colors.blueAccent,
),
child: SafeArea(
child: Material(
child: Container(
child: Column(
children: [
Column(
children: <Widget>[
new Stack(children: <Widget>[
new Container(
child: background.Row1
),
Container(
color: Colors.blueAccent,
child: Image.asset('assets/card_bg.png')
),
]
),
Container(
color: Colors.blueAccent,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ShowingOptions('assets/image1.png').Options,//*****calling function with parameter so that it can put widget**********//
ShowingOptions('assets/image2.png').Options,
ShowingOptions('assets/image3.png').Options,
ShowingOptions('assets/image4.png').Options,
],
),
background.Row2,
background.Row3
],
),
),
))
),
);
}
}
/**********function defination starts *************/
ShowingOptions(image) {
Widget Options = padding: EdgeInsets.only(bottom: 5, left: 7,
right: 7, top: 5),
child: Container(
height: 55.0,
width: 55.0,
child: Padding(
padding: EdgeInsets.all(1),
child: CircleAvatar(
backgroundColor: Colors.transparent,
radius: 10,
child: new Image.asset(image, height: 150, width:
150),
)),
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
border: new Border.all(
color: Colors.orange,
width: 5.0,
),
)
),
);
}
}
/**********function defination ends *************/
What Iam doing is making a funciton and when I am calling the function 'showOptions('assets/image1')' I am passing the image that I need to show.
Inside the function defination I am writing a widget that I wanted to be placed whenevr I call that funcition bys showing the image that I have passed
the way I implemented this whole is not working want a solution. I know this is not the proper way as I am new I would like to have some guidance.
Create a Custom Widget,
Create a Stateless or Stateful Class
declare Required Vairables
return your Custom Widget
below is Example of CustomButton with onPressed event.
//Create a Stateless or Stateful Class
class CustomButton extends StatelessWidget {
//declare Required Vairables
final String buttonText;
final VoidCallback onPressed;
final bool loading;
//constructor
CustomButton({this.buttonText,this.onPressed,this.loading});
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 30,right: 30),
child: Container(
decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(100)),
color: Colors.red),
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.all(Radius.circular(100)),
splashColor: Colors.green,
onTap: onPressed,
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Center(child:
loading==true?
CircularProgressIndicator(backgroundColor: Colors.white,)
:
Text(buttonText,style: TextStyle(fontSize: 30,color: Colors.white),)),
),
),
),
),
),
),
],
);
}
}
Use :
CustomButtonSmall(buttonText: "Direction",onPressed: (){})
Here is a wonderfully explained example of how to achieve this easily.
I just copy the code from there. You basically have to create a separate class for the widget you have in mind:
import 'package:flutter/material.dart';
class AppTextFormField extends StatelessWidget {
//
AppTextFormField({
this.controller,
this.hintText,
this.helpText,
this.prefixIcon,
this.suffixIcon,
this.isPassword,
this.enabled,
this.readOnly,
this.borderColor,
});
final TextEditingController controller;
final String hintText;
final String helpText;
final IconData prefixIcon;
final IconData suffixIcon;
final bool isPassword;
final bool enabled;
final bool readOnly;
final Color borderColor;#override
Widget build(BuildContext context) {
return Container(
child: TextFormField(
controller: controller,
readOnly: null == readOnly ? false : true,
obscureText: null == isPassword ? false : true,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.greenAccent,
width: 1.0,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.greenAccent,
width: 1.0,
),
),
border: OutlineInputBorder(
borderSide: BorderSide(
color: null == borderColor ? Colors.teal : borderColor,
width: 1.0,
),
),
hintText: null == hintText ? '' : hintText,
helperText: null == helpText ? '' : helpText,
prefixIcon: null == prefixIcon ? null : Icon(prefixIcon),
suffix: null == suffixIcon ? null : Icon(suffixIcon),
enabled: null == enabled ? true : false,
),
),
);
}
}
And then in the class where you want to use the widget you call the it like this:
Container(
child: Column(
children: [
AppTextFormField(
controller: _emailController,
helpText: 'Email',
hintText: 'Email',
prefixIcon: Icons.email,
),
AppTextFormField(
controller: _passwordController,
helpText: 'Password',
hintText: 'Password',
isPassword: true,
prefixIcon: Icons.lock_open,
),
You don't have to use all the methods, just the ones you need.