Related
I have a ListTile of cards going down my page but I don't have an AppBar in my app, so the ListTile starts at the top of the page. If I add padding, it adds it to each card in the ListView and then whilst I can position the cards lower in the page, I get huge gaps between them. Is there any way in which I can maintain the small gap that I would like in between the cards but simply have a larger gap at the top before the ListView begins? For reference, I pasted the code below. (Essentially it is a Todo list type app, so there is a FormField that pops ups from the bottom and you enter a task it appears on the card in the ListView.)
Widget _buildTodoList() {
DateTime now = DateTime.now();
String formattedDate = DateFormat('EEE d MMM').format(now);
return ListView.builder(
itemBuilder: (context, index) {
if(index < _todoItems.length) {
return ListTile(
onLongPress: () => _promptRemoveTodoItem(index),
title: Card(
elevation: 1.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(23.0),
),
semanticContainer: true,
clipBehavior: Clip.antiAlias,
child: Column(
children: <Widget>[
Container(
child: Stack(
children: <Widget>[
Container(
margin: EdgeInsets.fromLTRB(23.5, 27.5, 0.0, 0.0),
child: Text("?",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: Colors.black54,
),
),
),
Container(
margin: EdgeInsets.fromLTRB(23.5, 58.0, 0.0, 0.0),
child: Text("${_todoItems[index]}",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12,
color: Colors.black38,
),
),
),
Align(
alignment: Alignment.centerRight,
child: Container(
margin: EdgeInsets.fromLTRB(0.0, 6.5, 20.0, 0.0),
child: Text(formattedDate,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 11,
color: Colors.deepOrange[700],
),
),
),
),
],
),
height: 100,
width: 380,
),
],
),
),
);
}
},
);
}
I'm not sure if I understand your problem correct, but if it's - I see a couple of solutions:
You can add padding to whole ListView
Padding(padding: EdgeInsets.only(top: 200.0), child: ListView.builder(...),)
You can add empty item on first place in list:
ListView.builder(itemBuilder: (context, index) {
if (index == 0) {
return SizedBox(height: 200.0,);
} else {
// todo return your ListTile
}
},
itemCount: _todoItems.length + 1,)
Good morning. I made this screen and it works like a charm on high resolutions. Problem is, I want to support lower resolutions like Nexus 4 (768x1280).
So when I run this on an emulator (Nexus 4 size) and I touch the input fields, the keyboard either: blocks the inputs, or moves the two buttons at the bottom and overlaps them on something else.
So, to solve it I wrapped the entire layout on a ListView(), but now the bottom buttons, which are wrapped on a Row(), just won't stay at the bottom.
This is my code without the ListView and working on high res, but not on low:
return Form(
Stack(
Center(
Column(
...
),
),
Align(
alignment: Alignment.bottomCenter,
child: Row(
...
),
),
),
);
This is how it looks with resizeToAvoidBottomPadding: false
And this is how it looks with the value set to true
Thanks, everyone.
Did you try to use SingleChildScrollView ?
It is a widget for making the page able to resize the height and as well scrolling , for the listview thats not what is the listview for , you should add listview when you get an unknown amount of data,
We don't normally use it to just adjust the view.
ok so i edited the code as in here and i can scroll normally after the keyboard pop up and the view is good and i can click on the button normally try it.
Widget formWidget(){
return new Scaffold(
// appBar: AppBar(
// // Here we take the value from the MyHomePage object that was created by
// // the App.build method, and use it to set our appbar title.
// title: Text(widget.title),
// ),
body:Column(
children: <Widget> [
Expanded(
child:SingleChildScrollView(
child: Form(
child:
// Stack(
// children: <Widget>[
// Center(
// child: SingleChildScrollView(
// child:
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: (MediaQuery.of(context).size.height) > 600
? const EdgeInsets.only(top: 0.0)
: const EdgeInsets.only(top: 30.0),
child: Image(
image: AssetImage('assets/favIcon.png'),
width: 88.0,
height: 88.0,
),
),
Padding(
padding: const EdgeInsets.fromLTRB(0, 28.0, 0, 12.0),
child: Text(
'Delegados',
style: TextStyle(
fontFamily: 'Archia',
fontSize: 32.0,
),
),
),
Text(
'Introduce tus datos de acceso aquí.',
style: TextStyle(
color: Color(0xff83868F),
fontFamily: 'Brutal',
fontSize: 14.0,
),
),
Padding(
padding:
const EdgeInsets.fromLTRB(16.0, 26.0, 16.0, 12.0),
child: TextFormField(
decoration: InputDecoration(
labelText: 'Correo',
filled: true,
fillColor: Color(0xffF0F1F5),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
width: 0,
style: BorderStyle.none,
),
),
),
style: TextStyle(
fontFamily: 'Brutal',
color: Color(0xff1A1B1F),
),
controller: TextEditingController() ,
textInputAction: TextInputAction.next,
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 0, 16.0, 12.0),
child: TextFormField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: 'Contraseña',
filled: true,
fillColor: Color(0xffF0F1F5),
suffixIcon: Icon(Icons.remove_red_eye),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
borderSide: BorderSide(
width: 0,
style: BorderStyle.none,
),
),
),
style: TextStyle(
fontFamily: 'Brutal',
color: Color(0xff1A1B1F),
),
controller: TextEditingController() ,
//focusNode:FocusNode(),
obscureText: true,
),
),
Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 26.0),
child: CupertinoButton(
onPressed: () {},
child: Text(
'Olvidé mi Contraseña',
style: TextStyle(
color: Color(0xff565A66),
fontFamily: 'Brutal',
fontSize: 14.0,
),
),
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 0, 16.0, 0),
child: ButtonTheme(
minWidth: double.infinity,
child: InkWell(
// onTap: state is! LoginLoading
// ? _onLoginButtonPressed
// : null,
child: Container(
height: 48.0,
width: 500.0,
decoration: BoxDecoration(
color: Color(0xff00CC36),
borderRadius: BorderRadius.circular(8.0),
),
child: Align(
alignment: Alignment.center,
child: Container(
child:
// state is LoginLoading
// ? CircularProgressIndicator():
Text(
'INGRESAR AHORA ›',
style: TextStyle(
color: Colors.white,
fontFamily: 'Brutal',
fontSize: 14.0,
),
),
)),
),
),
),
),
],
),
// ),
)
,
// aqui va <<<<<<<<<<<<
// ],
// ),
)
),
Align(
alignment: Alignment.bottomCenter,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CupertinoButton(
onPressed: () {},
child: Text(
'¿No tienes cuenta?',
style: TextStyle(
color: Color(0xff83868F),
fontFamily: 'Brutal',
fontSize: 14.0,
),
),
),
InkWell(
onTap: null,
child: Container(
height: 32.0,
width: 112.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
border: Border.all(
color: Color(0xffD7D9E0),
width: 1.0,
),
),
child: Align(
alignment: Alignment.center,
child: Text(
'CONTACTANOS',
style: TextStyle(
color: Color(0xff565A66),
fontFamily: 'Brutal',
fontSize: 11.0),
),
),
),
),
],
),
),
]
)
,
// )
);
}
In your Scaffold can you try this.
Scaffold(
resizeToAvoidBottomInset: false,
...
);
I position two elements in the center and bottom of the screen by this code.
body: Column(
children: [
Expanded(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 80,
width: 80,
child:
Image(image: AssetImage("assets/images/logo.png"))),
SizedBox(height: 10),
Text(
"Tings App",
style: TextStyle(fontSize: 36),
),
SizedBox(height: 10),
TextFormField(
controller: _emailController,
decoration: InputDecoration(
border: OutlineInputBorder(),
isDense: true,
labelText: 'Email/phone'),
validator: (value) {
if (value.isEmpty) {
return 'Please enter a valid email';
}
return null;
},
),
],
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
_isLoading
? CircularProgressIndicator()
: SizedBox(
width: MediaQuery.of(context).size.width * 0.7,
height: 60.0,
child: ElevatedButton(
style: ButtonStyle(
shape: MaterialStateProperty.all<
RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100.0),
),
),
),
child: Text(
"SignIn",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w800,
fontSize: 18),
),
onPressed: () {
_login(context);
},
),
),
SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Don't have account?",
style: TextStyle(fontSize: 16),
),
SizedBox(width: 6),
TextButton(
onPressed: () {
Navigator.push(
context, SlideRightRoute(page: Signup()));
},
child: Text("SignUp"),
),
],
)
],
),
),
)
],
),
I want to use Appbar with text "Back" button I a using below code but
"Back" is coming in two line like below, also Appbar title is moving down side.
Ba
ck
Flutter code for same
final topAppBar = AppBar(
// elevation: 0.1,
backgroundColor: Color.fromRGBO(0, 113, 188, 1.0),
title: Text(
"MyAppBar",
style: TextStyle(
color: Colors.white,
fontFamily: 'Raleway-ExtraBold',
fontWeight: FontWeight.w900,
fontSize: 20.0,
),
),
leading: Padding(
padding: const EdgeInsets.only(left: 0),
child: FlatButton(
child: Text(
"Back",
// textDirection: TextDirection.ltr,
style: TextStyle(
color: Colors.white,
fontFamily: "Raleway-Medium",
fontSize: 14.0,
),
),
),
),
);
Is there any thing which I am missing here??
Use - FittedBox - fit: property to adjust leading widget.
leading: FittedBox(
fit: BoxFit.cover,
child: FlatButton(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, // add this to remove padding.
onPressed: () {},
child: Text(
"Back",
// textDirection: TextDirection.ltr,
style: TextStyle(
color: Colors.white,
fontFamily: "Raleway-Medium",
fontSize: 14.0,
),
),
),
),
Use leadingWidth with enough width.
leadingWidth: 80,
leading: Padding(
padding: const EdgeInsets.only(left: 0),
child: FlatButton(
child: Text(
"Back",
// textDirection: TextDirection.ltr,
style: TextStyle(
color: Colors.white,
fontFamily: "Raleway-Medium",
fontSize: 14.0,
),
),
),
UI stutters whenever CupertinoDatePicker is rotated due to using the picker in a FormField and calling formFieldState.didChange() on the picker's onDateTimeChanged
I'm trying to use Cupertino input fields in my app.
I've followed the steps in the Flutter Gallery to use CupertinoDatePicker. However, I'm wrapping the input fields inside a Form for validation.
This works, as I was able to tie the state of the picker to the FormFieldState. However, UI performance is abysmal. Every time the picker is rotated, I experience stutters. This is due to continuous call of state.didChange() on onDateTimeChanged
I tried calling state.didChange() only after the modal popup was dismissed, and it got rid of the stutters. But I want the state of the FormField to update as the user spins the picker.
Here's my code snippet.
Widget _getCupertinoDateTimeFormField(FormFieldState<DateTime> state) {
return GestureDetector(
onTap: () async {
await showCupertinoModalPopup<void>(
context: state.context,
builder: (BuildContext context) {
return _buildBottomPicker(
CupertinoDatePicker(
mode: CupertinoDatePickerMode.date,
initialDateTime: state.value?? DateTime.now(),
onDateTimeChanged: (DateTime newDateTime) {
state.didChange(newDateTime);
},
)
);
},
);
},
child: cupertinoDecoration(state, description,
Text(
_formatUI(state.value),
style: const TextStyle(color: CupertinoColors.inactiveGray),
),
),
);
}
Widget _buildBottomPicker(Widget picker) {
return Container(
height: _kPickerSheetHeight,
padding: const EdgeInsets.only(top: 6.0),
color: CupertinoColors.white,
child: DefaultTextStyle(
style: const TextStyle(
color: CupertinoColors.black,
fontSize: 22.0,
),
child: GestureDetector(
// Blocks taps from propagating to the modal sheet and popping.
onTap: () {},
child: SafeArea(
top: false,
child: picker,
),
),
),
);
}
Widget cupertinoDecoration <T> (FormFieldState<T> state, String description, Widget field) {
bool isError = state.errorText != null;
Widget label = Padding(
padding: EdgeInsets.symmetric(vertical: 1.0),
child: Text(
description,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14.0,
color: CupertinoColors.black,
),
),
);
List<Widget> fieldWidgets = [label, field];
if (isError) {
// add our error message
fieldWidgets.add(Text(
state.errorText,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
color: CupertinoColors.destructiveRed,
fontSize: 12.0,
),
));
}
return Container(
decoration: const BoxDecoration(
color: CupertinoColors.white,
border: Border(
bottom: BorderSide(
color: CupertinoColors.inactiveGray, width: 0.0),
),
),
child: Padding(
padding:
const EdgeInsets.all(6.0),
child: SafeArea(
top: false,
bottom: false,
child: DefaultTextStyle(
style: const TextStyle(
letterSpacing: -0.24,
fontSize: 17.0,
color: CupertinoColors.black,
),
child: Align(
alignment: Alignment.centerLeft,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: fieldWidgets,
),
),
),
),
),
);
}
Am I out of luck here? Are we not supposed to link the state of the picker to the state of the FormField?
Thanks in advance!
How to make a widget indicating the progress look like the attached image?
Well, you can actually create it yourself by extending a CustomPainter class and then insert it in your widget tree with a CustomPaint widget.
I've actually managed to provide an example for you, feel free to tweak it for your needs. The result is something like this:
class MyDeliveryProgress extends StatefulWidget {
_StepperState createState() => _StepperState();
}
class _StepperState extends State<MyDeliveryProgress> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.only(left: 10.0, right: 10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: RichText(
text: TextSpan(
text: 'Ordered',
style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
children: [
TextSpan(
text: '\n20 Dec',
style: TextStyle(fontWeight: FontWeight.normal),
),
]),
textAlign: TextAlign.center,
)),
//Text('Ordered'),
Expanded(
flex: 3,
child: IntrinsicHeight(
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Text(
'Shipped (${DateTime.now().day.toString()} Dec)',
style: TextStyle(fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 10.0),
child: CustomPaint(
painter: MyProgressLine(
estDelivery: DateTime(2019, 1, 1),
shipped: DateTime(2018, 12, 20),
),
child: Container(
width: double.infinity,
height: 5.0,
),
),
),
),
],
),
)),
Expanded(
child: RichText(
text: TextSpan(
text: 'Delivery',
style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
children: [
TextSpan(
text: '\n1 Jan',
style: TextStyle(fontWeight: FontWeight.normal),
),
]),
textAlign: TextAlign.center,
)),
],
),
)));
}
}
class MyProgressLine extends CustomPainter {
MyProgressLine({this.shipped, this.estDelivery});
final DateTime shipped;
final DateTime estDelivery;
#override
void paint(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.green
..strokeWidth = 3.0
..style = PaintingStyle.stroke;
double endPointsRadius = 5.0;
double width = size.width;
int totalDays = ((estDelivery.millisecondsSinceEpoch - shipped.millisecondsSinceEpoch) / 86400000).floor();
int currentDay = ((DateTime.now().millisecondsSinceEpoch - shipped.millisecondsSinceEpoch) / 86400000).floor();
double stepPerDay = width / totalDays;
// Draws starting point
canvas.drawCircle(Offset.zero, endPointsRadius, paint);
canvas.drawLine(Offset(endPointsRadius, 0.0), Offset(endPointsRadius + stepPerDay * currentDay, 0.0), paint);
// Draws current progress line
paint.style = PaintingStyle.fill;
canvas.drawCircle(Offset(endPointsRadius + stepPerDay * currentDay, 0.0), 3.0, paint);
// Draws endpoint
paint.style = PaintingStyle.stroke;
paint.color = Colors.grey.withOpacity(0.5);
canvas.drawLine(Offset(endPointsRadius + stepPerDay * currentDay, 0.0), Offset(stepPerDay * totalDays, 0.0), paint);
canvas.drawCircle(Offset((stepPerDay * totalDays) + endPointsRadius, 0.0), endPointsRadius, paint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}