Related
this is two search and dropdown sections I have implemented using animated_custom_dropdown.
I want that "Get Quote Filter " button to place next to the(right side) set location drop down..................................................................................................................................
........................................................................................................................................
import 'package:animated_custom_dropdown/custom_dropdown.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../constants/colors.dart';
const _labelStyle = TextStyle(fontWeight: FontWeight.w600);
class FantomSearch extends StatefulWidget {
const FantomSearch({Key? key}) : super(key: key);
#override
State<FantomSearch> createState() => _FantomSearchState();
}
class _FantomSearchState extends State<FantomSearch> {
final formKey = GlobalKey<FormState>();
final List<String> list = ['Heating', 'Electricians', 'Repair or Service', 'Accessibility Planner'];
final jobRoleFormDropdownCtrl = TextEditingController(),
jobRoleSearchDropdownCtrl = TextEditingController();
#override
void dispose() {
jobRoleFormDropdownCtrl.dispose();
jobRoleSearchDropdownCtrl.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
return Scaffold(
//backgroundColor:AppGreen,
appBar: AppBar(
systemOverlayStyle: SystemUiOverlayStyle.dark.copyWith(
statusBarColor: AppGreen,
),
backgroundColor: AppGreen,
elevation: .10,
),
body: Container(
height: 200,
color: AppGreen,
child: ListView(
padding: const EdgeInsets.all(16.0),
children: [
CustomDropdown.search(
hintText: 'Search Services',
items: list,
controller: jobRoleSearchDropdownCtrl,
fillColor: DarkGreen,
),
const SizedBox(height: 24),
// using form for validation
Form(
key: formKey,
child: Padding(
padding: const EdgeInsets.only(right: 150),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomDropdown(
hintText: 'Set Location',
items: list,
controller: jobRoleFormDropdownCtrl,
excludeSelected: false,
fillColor: DarkGreen,
),
const SizedBox(height: 16),
SizedBox(
child: ElevatedButton(
onPressed: () {
if (!formKey.currentState!.validate()) {
return;
}
},
child: const Text(
'Get Quotes filter',
style: TextStyle(fontWeight: FontWeight.w600),
),
style: ElevatedButton.styleFrom(primary: ContainerGreen),
),
),
],
),
),
),
],
),
),
);
}
}
From your code, I believe that currently "Get quotes filter" showing below to the "Set Location" correct?
If this is the issue, you need to update Column widget to Row which is inside Padding.
Like,
Container(
height: 200,
child: SingleChildScrollView(
child: Column(
children: [
/**/
Row(
children: [
Expanded(
child: CustomDropdown.search(
hintText: 'Search Services',
items: list,
controller: jobRoleSearchDropdownCtrl,
fillColor: DarkGreen,
),
),
Padding(
padding: EdgeInsets.only(left: 15, top: 20, right: 15, bottom: 20),
child: Text(
"cancel"
),
)
],
),
const SizedBox(height: 24),
// using form for validation
Padding(
padding: const EdgeInsets.only(right: 70),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
/**/
Expanded(
child: CustomDropdown(
hintText: 'Set Location',
items: list,
controller: jobRoleFormDropdownCtrl,
excludeSelected: false,
fillColor: DarkGreen,
),
),
const SizedBox(width: 16),
SizedBox(
child: ElevatedButton(
onPressed: () {
if (!formKey.currentState!.validate()) {
return;
}
},
child: const Text(
'Get Quotes filter',
style: TextStyle(fontWeight: FontWeight.w600),
),
style: ElevatedButton.styleFrom(primary: Colors.green),
),
),
],
),
),
],
),
),
)
If this still not worked, please share the expected output and what you are getting now. Because I am not able to compile your code due to custom widgets.
I have updated the color so please update it as per your need. The output is something like,
So, I'm really new to flutter or dart. I looked at many tutorials, a bit hard to learn.
I need to know if I can, and how can I add more containers that contain Texts or Button in Flutter.
I am building a login page which has a container for some text and form field. I want to insert a second container where I can insert a button on the bottom of login which says 'GET OTP' has the max width the display offers as shown in the image of Login Page.
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class LoginPage extends StatefulWidget {
#override
LoginPageState createState() => LoginPageState();
}
class LoginPageState extends State<LoginPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(height: 180.0,),
Padding(padding: EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Lets Start With Your', style: GoogleFonts.yantramanav(fontSize: 26.0, fontWeight: FontWeight.w300, textStyle: TextStyle(color: Colors.indigo[900])),),
Text('Phone / Email', style: GoogleFonts.robotoCondensed(fontSize: 50.0, fontWeight: FontWeight.w700, textStyle: TextStyle(color: Colors.indigo[900])),),
Padding(padding: EdgeInsets.only(top: 20.0),
child: TextFormField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Enter phone number/email id',
hintText: 'Enter phone number/email id',
)
),
Padding(
padding: EdgeInsets.only(top: 170.0),
child: Center(
child: Column(children: <Widget>[
OutlineButton(onPressed: () {}, child: Text('Skip Login', style: TextStyle(color: Colors.grey[500] ),),
borderSide: BorderSide( color: Colors.grey[300], width: 2.0,),
),
],),), ) ,
],
)
)
]
)
),
**# I want to add a container (to insert a button with full width of the display) here**
],)
);
}
}
One of the solutions is to use the Stack widget, that gives you great freedom to position widgets:
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Container(...), //your first container
Positioned( //used to position the given child
bottom: 0,
child: GestureDetector(
onTap: () {
//here what you want to do when the user press the button
},
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.all(10),
width: MediaQuery.of(context).size.width, //to give the container the width of the screen
child: Text(
'GET OTP',
style: TextStyle(
color: Colors.white,
fontSize: 22,
),
),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.purple,
Colors.blue, //Linear Gradient with 3 colors
Colors.blue[800],
],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
),
),
),
),
],
),
);
}
The result:
I have a container on my page which should have either image or text depending on the one that is given in the Custom Dialog widget. I have created this widget in a separate Stateless Class. I am calling this from the page where my container is created.
Custom Dialog
class CustomDialog extends StatelessWidget {
TextEditingController textController;
BuildContext context;
Function saveAction;
CustomDialog({
this.context,
this.textController,
this.saveAction,
});
_getText(String text) {
return Text(
text,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.black, fontSize: 16),
);
}
_buildIconHolder(IconData icon) {
return Container(
width: 60,
height: 60,
decoration:
BoxDecoration(shape: BoxShape.circle, color: Colors.yellowAccent),
child: Icon(icon, size: 40, color: Colors.blue),
);
}
_buildImagePickerElements() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_getText("Add Image"),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
_buildIconHolder(Icons.folder_open),
_getText("or"),
_buildIconHolder(Icons.camera_alt),
],
),
],
);
}
_buildTextInput() {
return Card(
color: Colors.orange[600],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20))),
child: Padding(
padding: EdgeInsets.all(10),
child: TextField(
controller: textController,
decoration: InputDecoration(
border: InputBorder.none, hintText: "hint text here"),
keyboardType: TextInputType.emailAddress,
textAlign: TextAlign.center,
textInputAction: TextInputAction.next,
maxLines: 4,
),
),
);
}
_buildPrimaryButton(Function fn) {
return InkWell(
child: GestureDetector(
child: Container(
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.all(Radius.circular(10))),
child: Text("Save",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white)),
),
onTap: () => {
fn,
Navigator.of(ctx).pop();
),
);
}
_getDialogElements() {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
_buildImagePickerElements(),
SizedBox(height: 10),
Row(
children: <Widget>[
Expanded(
child: Padding(
padding: EdgeInsets.only(right: 10),
child: Divider(color: Colors.purple, thickness: 2),
),
),
_getText("or"),
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Divider(color: Colors.purple, thickness: 2),
),
),
],
),
SizedBox(height: 10),
_getText("Describe Below"),
SizedBox(height: 10),
_buildTextInput(),
SizedBox(height: 10),
_buildPrimaryButton(saveAction),
],
);
}
Widget build(BuildContext context) {
return AlertDialog(
backgroundColor: Colors.blueGrey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20)),
),
content: SingleChildScrollView(
child: Container(width: 400.0, child: _getDialogElements()),
),
);
}
}
HomePage
import 'package:custom_dialog/custom_dialog.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController _controller = new TextEditingController();
String _field = "";
clicked(BuildContext ctx) {
// setState(() {
if (_controller.text.isNotEmpty && _controller.text != null) {
_field = _controller.text;
}
// Navigator.of(ctx).pop();
// });
}
_makeContainerAText() {
return Container(
width: 40,
height: 60,
child: Text(_field),
);
}
_makeContainerAActionBtn() {
return Container(
width: 40,
height: 60,
child: FloatingActionButton(
backgroundColor: Colors.yellow,
onPressed: () {
setState(() {
print("Action button pressed");
showDialog(
context: context,
builder: (BuildContext context) => CustomDialog(
context: context,
textController: _controller,
saveAction: clicked(context),
),
);
});
},
child: Icon(
Icons.add,
color: Colors.purple,
),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Container Home Page"),
),
body: Center(
child: Stack(
children: <Widget>[
InkWell(
splashColor: Colors.orange[400],
highlightColor: Colors.purple,
child: Material(
elevation: 10,
borderRadius: BorderRadius.all(Radius.circular(10)),
child: DecoratedBox(
decoration: BoxDecoration(
shape: BoxShape.rectangle,
color: Colors.orange[600],
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: Padding(
padding: EdgeInsets.all(25),
child: validate
? _makeContainerAText()
: _makeContainerAActionBtn(),
),
),
),
)
],
),
),
);
}
}
In short, I want the container to look like the one given below in the link after adding text or image.
Container_Image_Text_Example
I cannot use setState in the custom dialog class since it is stateless. If I make it stateful, then I am unable to see it on the screen when I click on the container that executes the show dialog. I tried to use the setState in the clicked() method but ended up with the error below.
════════ Exception caught by animation library ═════════════════════════════════
The following assertion was thrown while notifying status listeners for AnimationController:
setState() or markNeedsBuild() called during build.
This _ModalScope<dynamic> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: _ModalScope<dynamic>-[LabeledGlobalKey<_ModalScopeState<dynamic>>#f69dd]
state: _ModalScopeState<dynamic>#32d94
The widget which was currently being built when the offending call was made was: Builder
dirty
When the exception was thrown, this was the stack
#0 Element.markNeedsBuild.<anonymous closure>
package:flutter/…/widgets/framework.dart:3896
#1 Element.markNeedsBuild
package:flutter/…/widgets/framework.dart:3911
#2 State.setState
package:flutter/…/widgets/framework.dart:1168
#3 _ModalScopeState._routeSetState
package:flutter/…/widgets/routes.dart:664
#4 ModalRoute.setState
package:flutter/…/widgets/routes.dart:784
...
The AnimationController notifying status listeners was: AnimationController#073ac(⏮ 0.000; paused; for _DialogRoute<dynamic>)
════════════════════════════════════════════════════════════════════════════════
════════ Exception caught by widgets library ═══════════════════════════════════
setState() or markNeedsBuild() called during build.
The relevant error-causing widget was
MaterialApp
lib/main.dart:9
════════════════════════════════════════════════════════════════════════════════
I've been trying to make the TextFormField ('email:') appear in my Simulator, but it's not appearing in my Simulator as following:
However when I make changes to any other things, it does change, so the Simulator is not a problem.
This is my login_page.dart file:
import 'package:flutter/material.dart';
class LoginPage extends StatefulWidget{
#override
State createState() => new LoginPageState();
}
class LoginPageState extends State<LoginPage>{
#override
Widget build(BuildContext context){
return new Scaffold(
appBar: AppBar(
title: new Text("SMART ID", textAlign: TextAlign.center, style: TextStyle(fontFamily: 'Open Sans', fontWeight: FontWeight.bold)),
leading: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset(
"assets/arrowPNG.png",
scale: 8.0,
)
)
),
backgroundColor: Colors.transparent,
body: Stack(
children: <Widget>[
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/background.png'),
fit: BoxFit.cover,
),
),
),
Positioned(
width: MediaQuery.of(context).size.width,
top: MediaQuery.of(context).size.width * 0.30,
child: Container(
margin: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset('assets/arrowPNG.png', scale: 2.5),
SizedBox(height: 20,),
Text("SMARTID", style: TextStyle(
fontSize: 30, color: Colors.black, fontFamily: 'Open Sans', fontWeight: FontWeight.bold,
),
),
Text("Attendance & Wallet Monitoring", style: TextStyle(
fontSize: 16, color: Colors.black, fontFamily: 'Open Sans', fontWeight: FontWeight.normal,
)
)
],
),
),
),
Positioned(
width: MediaQuery.of(context).size.width,
top: MediaQuery.of(context).size.width * 0.85,
child: Container(
margin: EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Version 1.0", style: TextStyle(
fontSize: 16, color: Colors.black, fontFamily: 'Open Sans', fontWeight: FontWeight.normal,
)
)
],
)
)
)
],
),
);
}
}
class LoginForm extends StatefulWidget{
#override
LoginFormState createState(){
return LoginFormState();
}
}
class LoginFormState extends State<LoginForm>{
final formKey = GlobalKey<FormState>();
String _username, _password;
#override
Widget build(BuildContext context){
body: Card(
child: Padding(
padding: EdgeInsets.all(8.0),
child: Form(
key: formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextFormField(
decoration: InputDecoration(
labelText: 'Username:'
),
validator: (input) => !input.contains('0') ? 'Username cannot be blank' : null,
onSaved: (input) => _username = input,
),
],
),
),
),
);
}
}
How do I resolve this? Thanks in advance!
You are right, Simulator is not a problem. The problem is in your code. You have correctly created the LoginPage. To correct the LoginForm widget, in your LoingFormState's build function replace:
body: Card(
// your usual code that you have above
)
with
return Card(
// your usual code that you have above
)
Also you have not used the LoginForm Widget in the LoginPage, and that is the reason the form is not visible. Simply, creating a widget doesn't include the widget on any screen, you have to use the widget.
You can do that by adding the widget inside the body section of LoginPageState's build function. To use the widget type
body: Stack(
children: <Widget>[
// your other widgets
LoginForm(),
]
)
like you do for any other widget.
This should solve your problem
I'm just getting started with Flutter.
What is the recommended way to layout a list of cards on a screen?
Some cards will have only contain a single object as a line of text, but others that contain multiple objects as lines of text should also have a header within the card.
For example, here is a mock-up that I drew that I'm trying to accomplish.
Flutter doesn't like a ListView inside a Card. It generates the following errors:
I/flutter (13243): ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter (13243): The following assertion was thrown during performResize():
I/flutter (13243): Vertical viewport was given unbounded height.
I/flutter (13243): Viewports expand in the scrolling direction to fill their container.In this case, a vertical
I/flutter (13243): viewport was given an unlimited amount of vertical space in which to expand. This situation
I/flutter (13243): typically happens when a scrollable widget is nested inside another scrollable widget.
I/flutter (13243): If this widget is always nested in a scrollable widget there is no need to use a viewport because
I/flutter (13243): there will always be enough vertical space for the children. In this case, consider using a Column
I/flutter (13243): instead. Otherwise, consider using the "shrinkWrap" property (or a ShrinkWrappingViewport) to size
I/flutter (13243): the height of the viewport to the sum of the heights of its children.
With #aziza's help, I banged out the following code with provides a base layout very close to what I mocked up, but I have a couple of questions:
Is this the most efficient use of nested widgets?
Is there any way to set a global font size so that I don't have to set it on every Text widget?
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My Layout',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'App Bar Title'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List itemList = [
'Card Text 2 Line 1',
'Card Text 2 Line 2',
'Card Text 2 Line 3',
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Sub Title',
style: TextStyle(
fontSize: 25.0,
),
),
],
),
),
Row(
children: [
Expanded(
child: Card(
shape: RoundedRectangleBorder(
side: BorderSide(
width: 3.0,
),
),
margin: EdgeInsets.all(15.0),
color: Colors.grey,
elevation: 10.0,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Card 1 Text',
style: TextStyle(
fontSize: 25.0,
),
),
),
),
),
],
),
Row(
children: [
Expanded(
child: Card(
shape: RoundedRectangleBorder(
side: BorderSide(
width: 3.0,
),
),
margin: EdgeInsets.all(15.0),
color: Colors.grey,
elevation: 10.0,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Card 2 Header',
style: TextStyle(
fontSize: 25.0,
),
),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: List.generate(
itemList.length,
(i) => Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
itemList[i],
style: TextStyle(
fontSize: 25.0,
),
),
),
),
),
],
),
),
),
],
),
Row(
children: [
Expanded(
child: Card(
shape: RoundedRectangleBorder(
side: BorderSide(
width: 3.0,
),
),
margin: EdgeInsets.all(15.0),
color: Colors.grey,
elevation: 10.0,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
'Card 3 Text',
style: TextStyle(
fontSize: 25.0,
),
),
),
),
),
],
)
],
),
),
);
}
}
CardModel: Represents each list item, which contains an optional header and list of strings. Building ListView: If header field is persent it is added in a column, then the list of strings of the card are added to the above column as well. At the end these individually created cards are wrapped as a list and displayed inside the ListView.
import 'package:flutter/material.dart';
void main() => runApp(
new MaterialApp(
debugShowCheckedModeBanner: false,
home: new CardsDemo(),
),
);
class CardsDemo extends StatefulWidget {
#override
_CardsDemoState createState() => new _CardsDemoState();
}
class _CardsDemoState extends State<CardsDemo> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Cards'),
),
body: new Column(
children: <Widget>[
new Center(
child: new Padding(
padding: const EdgeInsets.all(15.0),
child: new Text(
'Sub Title',
style:
new TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold),
),
),
),
new Expanded(
child: new ListView(
children: _buildCards(),
padding: const EdgeInsets.all(8.0),
),
),
],
),
);
}
Widget _buildCard(CardModel card) {
List<Widget> columnData = <Widget>[];
if (card.isHeaderAvailable) {
columnData.add(
new Padding(
padding: const EdgeInsets.only(bottom: 8.0, left: 8.0, right: 8.0),
child: new Text(
card.headerText,
style: new TextStyle(fontSize: 24.0, fontWeight: FontWeight.w500),
),
),
);
}
for (int i = 0; i < card.allText.length; i++)
columnData.add(
new Text(card.allText[i], style: new TextStyle(fontSize: 22.0),),
);
return new Card(
child: new Padding(
padding: const EdgeInsets.symmetric(vertical: 15.0),
child: Column(children: columnData),
),
);
}
List<Widget> _buildCards() {
List<Widget> cards = [];
for (int i = 0; i < sampleCards.length; i++) {
cards.add(_buildCard(sampleCards[i]));
}
return cards;
}
}
class CardModel {
final String headerText;
final List<String> allText;
final bool isHeaderAvailable;
CardModel(
{this.headerText = "", this.allText, this.isHeaderAvailable = false});
}
List<CardModel> sampleCards = [
new CardModel(allText: ["Card 1 Text"]),
new CardModel(
isHeaderAvailable: true,
headerText: "Card 2 Header",
allText: ["Card 2 Text Line 1", "Card 2 Text Line 2"]),
new CardModel(allText: ["Card 3 Text"]),
];