Related
I'm trying to create a contact book page that will load recent contact and all of my contact list, I managed to get the data and build it into the widget.
Design that I want to achieve for the screen body are:
a search widget which always stay on top even when the contact book can be scrolled.
List of the contact that can be scrolled
The problem is when there are a lot of contact (let say 20 contacts). If the screen size is small, it can only scroll until 7-8 contacts, but I know there's more to it. But I guess my ListView.Builder won't let me scroll down anymore. Like it's constrained by some properties but I don't know what is the reason
Can anyone please tell me the problem and how to fix this ?
Here's my code:
/// BODY
body: WillPopScope(
onWillPop: () {
print('hello');
},
child: ListView(
physics: BouncingScrollPhysics(),
shrinkWrap: true,
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 20),
height: MediaQuery.of(context).size.height,
child: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (context, isScrolled) {
return [
SliverPersistentHeader(
pinned: true,
floating: false,
delegate: ChooseUsersHeaderDelegate(
widgetList: Container(
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// Build Search Box Function
buildSearch(),
],
),
),
minHeight: selectedContactId.isNotEmpty ? 135 : 60,
maxHeight: selectedContactId.isNotEmpty ? 135 : 60,
parentContext: context,
),
),
];
},
body: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// All Contacts Subtitle
Text(
'allContacts',
style: GoogleFonts.montserrat(
fontSize: 14,
fontWeight: FontWeight.w500,
color: primaryColor,
fontStyle: FontStyle.normal,
),
).tr(),
/// All Contacts
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
isLoading
? ReusableLoadingContact()
: NotificationListener<
OverscrollIndicatorNotification>(
onNotification: (overScroll) {
overScroll.disallowIndicator();
return true;
},
/// Call function to return the widget for building contact list
child: buildWidget(context, 'contacts')),
],
),
],
),
),
),
)
],
),
),
Here's the code for building the contact List:
Widget build(BuildContext context) {
return contact.isEmpty
? Text('')
: ListView.builder(
/// Force to build only 2 items if recent, otherwise build all
itemCount: status == 'recent' ? contact.length > 2 ? 2 : contact.length : contact.length,
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
/// Return the widget of each contact
},
);
}
if you want to make your search bar stay on top here a very simple way
body : Column(
children:[
// this part will stay on top
Container() // your widget search bar
// listview widget will rendered after the Container
Expanded(
child: Listview.builder(
// this part will be scrollable
// return the widget contact
)
)
]
....
To attain this , use Container and Expanded as child widgets for Column
Format:
body : Column(
children:[
Container(
child: // Your search bar goes here
),
Expanded(
child: // Your list view goes here
)
)
]
I have created list of cateogories using list listview.builder
here I want to highlight selected category by underlining category text with container and I want to apply width based on text length...same way like we do underline for text,
I know they are inbuilt some packages but I don't want to use it as I want to implement my logic.
here is my code
I have set comment where I want to dynamic width
class CatogoryList extends StatefulWidget {
#override
State<CatogoryList> createState() => _CatogoryListState();
}
class _CatogoryListState extends State<CatogoryList> {
List<String> categories=['HandBag','Jwellery','FootWear','Dresses','Pens','Jeans','Trousers'];
int selectedindex=2;
#override
Widget build(BuildContext context) {
return SizedBox(
height: 30,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: categories.length,
itemBuilder: (context,index){
return buildCategory(index);
}),
);
}
Widget buildCategory(int index)
{
return GestureDetector(
onTap: (){
setState(() {
selectedindex=index;
});
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(categories[index],style: TextStyle(fontSize: 20,color: selectedindex==index?Colors.blue:Colors.grey),),
if(selectedindex==index)
Container(
// here I want to set widget of container based on text length
height: 3,width: 30,color: Colors.blue,),
],),
),
);
}
}
One way is to wrap the Column in an IntrinsicWidth and leave out the width from the Container. Like
Widget buildCategory(int index)
{
return GestureDetector(
onTap: (){
setState(() {
selectedindex=index;
});
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: IntrinsicWidth(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(categories[index],style: TextStyle(fontSize: 20,color: selectedindex==index?Colors.blue:Colors.grey),),
if(selectedindex==index)
Container(height: 3,color: Colors.blue,),
],),
),
),
);
}
I think the proper approach would be to wrap the selected Text widget with Container and apply the decoration there.
I'm answering your question as well:
final TextPainter textPainter = TextPainter(
text: TextSpan(text: text, style: yourTextStyle), /// apply your style here
textScaleFactor: MediaQuery.of(context).textScaleFactor,
textDirection: TextDirection.ltr,
)..layout();
final double width = textPainter.size.width;
Im trying to remove the scrolling of my ListView widget. Currently, the user only has a small window of tiles to scroll through. The listview data is paged so only shows 20 items at once- i want to show all 20 items and the whole page to scroll, rather than the user scroll through the list view a few at a time. How can i achieve this?
return Column(
children: [
Expanded(
child: ListView.separated(
separatorBuilder: (context, index) => Container(
height: 1,
color: Constants.listSeparatorGrey,
),
itemCount: pageState.data?.data?.length ?? 0,
itemBuilder: (context, idx) =>
itemBuilder(pageState.data?.data?[idx]),
),
),
Text('${pageState.data?.total ?? 0} results',
style: TextStyles.rowHeader(context)),
Text(
'Page ${pageState.data?.currentPage()} of ${pageState.data?.pageNum()}',
style: TextStyles.rowHeader(context),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(width: 20),
if (pageState.data?.hasPrev ?? false)
MyButton(
// isLoading: pageState is Loading,
type: ButtonType.secondary,
label: "Previous",
onPressed: pageApi.prev,
)
else
Container(width: 8),
const SizedBox(
width: 8,
),
(pageState.data?.hasNext ?? false)
? MyButton(
// isLoading: pageState is Loading,
type: ButtonType.secondary,
label: "Next",
onPressed: pageApi.next,
)
: const SizedBox(width: 8)
],
),
],
);
If I correctly understand you. Just move all your content to ListView. So all your page starts scrolling with content.
Widget header = Container(color: Colors.green, height: 100);
List<Widget> content = const [Text("firstCell"), Text("secondCell")];
Widget button =
const TextButton(onPressed: null, child: Text("Next page"));
List<Widget> widgets = [header] + content + [button];
return Scaffold(
appBar: AppBar(
title: const Text('All content in list'),
),
body: ListView(children: widgets),
);
Here result:
All content in ListView
I solved my problem.
I just put the root column in a SingleChildScrollView()
very simple in the end
I have a 2 column flutter DataTable and the lines don't span the screen width leaving lots of white space. I found this issue
https://github.com/flutter/flutter/issues/12775
That recommended wrapping the DataTable in a SizedBox.expand widget but that does not work produces RenderBox was not laid out:
SizedBox.expand(
child: DataTable(columns:_columns, rows:_rows),
),
Full widget
#override
Widget build(BuildContext context) {
return new Scaffold(
body:
SingleChildScrollView(
child: Column(
children: [Container(Text('My Text')),
Container(
alignment: Alignment.topLeft,
child: SingleChildScrollView(scrollDirection: Axis.horizontal,
child: SizedBox.expand(
child: DataTable(columns:_columns, rows:_rows),
),
),
),
]))
);
}
You can add the crossAxisAlignment for your Column to strech
crossAxisAlignment: CrossAxisAlignment.stretch
SizedBox.expand results in the DataTable taking an infinite height which the SingleChildScrollView won't like. Since you only want to span the width of the parent, you can use a LayoutBuilder to get the size of the parent you care about and then wrap the DataTable in a ConstrainedBox.
Widget build(BuildContext context) {
return Scaffold(
body: LayoutBuilder(
builder: (context, constraints) => SingleChildScrollView(
child: Column(
children: [
const Text('My Text'),
Container(
alignment: Alignment.topLeft,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ConstrainedBox(
constraints: BoxConstraints(minWidth: constraints.minWidth),
child: DataTable(columns: [], rows: []),
),
),
),
],
),
),
),
);
}
This is an issue, incompleteness, in an otherwise beautiful Widget which is the DataTable,
I faced this issue in a production code, this solution worked on more than half of the lab devices:
ConstrainedBox(
constraints: BoxConstraints.expand(
width: MediaQuery.of(context).size.width
),
child: DataTable( // columns and rows.),)
But you know what suprisingly worked on %100 of the devices ? this:
Row( // a dirty trick to make the DataTable fit width
children: <Widget>[
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: DataTable(...) ...]//row children
Note: The Row has only one child Expanded which in turn enclose a SingleChildScrollView which in turn enclose the DataTable.
Note that this way you cant use SingleChileScrollView with scrollDirection: Axis.horizontal, in case you need it, but you dont otherwise this question would be irrelevant to your use case.
In case someone of the Flutter team reads this, please enrich the DataTable Widget, it will make flutter competitive and powerful, flutter may eclipse androids own native API if done right.
Set your datatable in Container and make container's width as double.infinity
Container(
width: double.infinity,
child: DataTable(
columns: _columns,
rows: _rows,
));
For DataTable widget this code has worked for me regarding dataTable width as match parent to device-width,
Code snippet:
ConstrainedBox(
constraints:
BoxConstraints.expand(
width: MediaQuery.of(context).size.width
),
child:
DataTable(
// inside dataTable widget you must have columns and rows.),)
and you can remove space between columns by using attribute like
columnSpacing: 0,
Note:
using ConstrainedBox widget solves your issue,
constraints: BoxConstraints.expand(width: MediaQuery.of(context).size.width),
Complete Code :
Note:
In this sample code, I covered sorting and editing DataTable widget concepts.
In Lib Folder you must have this class
main.dart
DataTableDemo.dart
customer.dart
main.dart class code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'DataTableDemo.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: DataTableDemo(),
);
}
}
DataTableDemo.dart class code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'customer.dart';
class DataTableDemo extends StatefulWidget {
DataTableDemo() : super();
final String title = "Data Table";
#override
DataTableDemoState createState() => DataTableDemoState();
}
class DataTableDemoState extends State<DataTableDemo> {
List<customer> users;
List<customer> selectedUsers;
bool sort;
TextEditingController _controller;
int iSortColumnIndex = 0;
int iContact;
#override
void initState() {
sort = false;
selectedUsers = [];
users = customer.getUsers();
_controller = new TextEditingController();
super.initState();
}
onSortColum(int columnIndex, bool ascending) {
if (columnIndex == 0) {
if (ascending) {
users.sort((a, b) => a.firstName.compareTo(b.firstName));
} else {
users.sort((a, b) => b.firstName.compareTo(a.firstName));
}
}
}
onSelectedRow(bool selected, customer user) async {
setState(() {
if (selected) {
selectedUsers.add(user);
} else {
selectedUsers.remove(user);
}
});
}
deleteSelected() async {
setState(() {
if (selectedUsers.isNotEmpty) {
List<customer> temp = [];
temp.addAll(selectedUsers);
for (customer user in temp) {
users.remove(user);
selectedUsers.remove(user);
}
}
});
}
SingleChildScrollView dataBody() {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: ConstrainedBox(
constraints: BoxConstraints.expand(width: MediaQuery.of(context).size.width),
child: DataTable(
sortAscending: sort,
sortColumnIndex: iSortColumnIndex,
columns: [
DataColumn(
label: Text("FIRST NAME"),
numeric: false,
tooltip: "This is First Name",
onSort: (columnIndex, ascending) {
setState(() {
sort = !sort;
});
onSortColum(columnIndex, ascending);
}),
DataColumn(
label: Text("LAST NAME"),
numeric: false,
tooltip: "This is Last Name",
),
DataColumn(label: Text("CONTACT NO"), numeric: false, tooltip: "This is Contact No")
],
columnSpacing: 2,
rows: users
.map(
(user) => DataRow(
selected: selectedUsers.contains(user),
onSelectChanged: (b) {
print("Onselect");
onSelectedRow(b, user);
},
cells: [
DataCell(
Text(user.firstName),
onTap: () {
print('Selected ${user.firstName}');
},
),
DataCell(
Text(user.lastName),
),
DataCell(Text("${user.iContactNo}"),
showEditIcon: true, onTap: () => showEditDialog(user))
]),
)
.toList(),
),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
// verticalDirection: VerticalDirection.down,
children: <Widget>[
Expanded(
child: Container(
child: dataBody(),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: EdgeInsets.all(20.0),
child: OutlineButton(
child: Text('SELECTED ${selectedUsers.length}'),
onPressed: () {},
),
),
Padding(
padding: EdgeInsets.all(20.0),
child: OutlineButton(
child: Text('DELETE SELECTED'),
onPressed: selectedUsers.isEmpty ? null : () => deleteSelected(),
),
),
],
),
],
),
),
);
}
void showEditDialog(customer user) {
String sPreviousText = user.iContactNo.toString();
String sCurrentText;
_controller.text = sPreviousText;
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: new Text("Edit Contact No"),
content: new TextFormField(
controller: _controller,
keyboardType: TextInputType.number,
decoration: InputDecoration(labelText: 'Enter an Contact No'),
onChanged: (input) {
if (input.length > 0) {
sCurrentText = input;
iContact = int.parse(input);
}
},
),
actions: <Widget>[
new FlatButton(
child: new Text("Save"),
onPressed: () {
setState(() {
if (sCurrentText != null && sCurrentText.length > 0) user.iContactNo = iContact;
});
Navigator.of(context).pop();
},
),
new FlatButton(
child: new Text("Cancel"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}
customer.dart class code
class customer {
String firstName;
String lastName;
int iContactNo;
customer({this.firstName, this.lastName,this.iContactNo});
static List<customer> getUsers() {
return <customer>[
customer(firstName: "Aaryan", lastName: "Shah",iContactNo: 123456897),
customer(firstName: "Ben", lastName: "John",iContactNo: 78879546),
customer(firstName: "Carrie", lastName: "Brown",iContactNo: 7895687),
customer(firstName: "Deep", lastName: "Sen",iContactNo: 123564),
customer(firstName: "Emily", lastName: "Jane", iContactNo: 5454698756),
];
}
}
Simple Answer:
Wrap your datatable with a Container() with width: double.infinity().
Container(
width: double.infinity,
child: DataTable(
..
.
My Prefered Way
You can use DataTable 2 Package at pub.dev https://pub.dev/packages/data_table_2
This package will give you the DataTable2() widget which will expand to the available space by default. Also you get more options like ColumnSize etc.
just wrap your DataTable with Sizedbox and give width to double.infinity.
SizedBox(
width: double.infinity,
child: DataTable()
)
Just wrap the data table with a container having fixed width defined and everything should work.
Even when you need multiple tables in one screen this worked well for me as of flutter 2.2.3.
final screenWidth = MediaQuery.of(context).size.width;
Scaffold(
body: SingleChildScrollView(child:Container(
child: Column(
children: [
Container(
width: screenWidth, // <- important for full screen width
padding: EdgeInsets.fromLTRB(0, 2, 0, 2),
child: buildFirstTable() // returns a datatable
),
Container(
width: screenWidth, // <- this is important
padding: EdgeInsets.fromLTRB(0, 2, 0, 2),
child: buildSecondTable() // returns a datatable
)
])
))
)
This also works for single table just wrap with container with desired width.
SingleChildScrollView(
child: Card(
child: SizedBox(
width: double.infinity,
child: DataTable(columns:_columns, rows:_rows),
),
),
),
How to vertically center a column in Flutter? I have used widget "new Center". I have used widget "new Center", but it does not vertically center my column ? Any ideas would be helpful....
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Thank you"),
),
body: new Center(
child: new Column(
children: <Widget>[
new Padding(
padding: new EdgeInsets.all(25.0),
child: new AnimatedBuilder(
animation: animationController,
child: new Container(
height: 175.0,
width: 175.0,
child: new Image.asset('assets/angry_face.png'),
),
builder: (BuildContext context, Widget _widget) {
return new Transform.rotate(
angle: animationController.value * 6.3,
child: _widget,
);
},
),
),
new Text('We are glad we could serve you...', style: new TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w600,
color: Colors.black87),),
new Padding(padding: new EdgeInsets.symmetric(vertical: 5.0, horizontal: 0.0)),
new Text('We appreciate your feedback ! !', style: new TextStyle(
fontSize: 13.0,
fontWeight: FontWeight.w200,
color: Colors.black87),),
],
),
),
);
}
Solution as proposed by Aziz would be:
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
//your widgets here...
],
)
It would not be in the exact center because of padding:
padding: EdgeInsets.all(25.0),
To make exactly center Column - at least in this case - you would need to remove padding.
Try:
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children:children...)
Try this one. It centers vertically and horizontally.
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: children,
),
)
With Column, use:
mainAxisAlignment: MainAxisAlignment.center
It align its children(s) to center of its parent Space vertically
You control how a row or column aligns its children using the mainAxisAlignment and crossAxisAlignment properties. For a row, the main axis runs horizontally and the cross axis runs vertically. For a column, the main axis runs vertically and the cross axis runs horizontally.
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
While using Column, use this inside the column widget :
mainAxisAlignment: MainAxisAlignment.center
It align its children(s) to the center of its parent Space is its main axis i.e. vertically
or,
wrap the column with a Center widget:
Center(
child: Column(
children: <ListOfWidgets>,
),
)
if it doesn't resolve the issue wrap the parent container with a Expanded widget..
Expanded(
child:Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: children,
),
),
)
Another Solution!
If you want to set widgets in center vertical form, you can use ListView for it.
for eg: I used three buttons and add them inside ListView which followed by
shrinkWrap: true -> With this ListView only occupies the space which needed.
import 'package:flutter/material.dart';
class List extends StatelessWidget {
#override
Widget build(BuildContext context) {
final button1 =
new RaisedButton(child: new Text("Button1"), onPressed: () {});
final button2 =
new RaisedButton(child: new Text("Button2"), onPressed: () {});
final button3 =
new RaisedButton(child: new Text("Button3"), onPressed: () {});
final body = new Center(
child: ListView(
shrinkWrap: true,
children: <Widget>[button1, button2, button3],
),
);
return new Scaffold(
appBar: new AppBar(
title: Text("Sample"),
),
body: body);
}
}
void main() {
runApp(new MaterialApp(
home: List(),
));
}
Output:
CrossAlignment.center is using the Width of the 'Child Widget' to center itself and hence gets rendered at the start of the page.
When the Column is centered within the page body's 'Center Container' , the CrossAlignment.center uses page body's 'Center' as reference and renders the widget at the center of the page
Code
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(
title:"DynamicWidgetApp",
home:DynamicWidgetApp(),
));
class DynamicWidgetApp extends StatefulWidget{
#override
DynamicWidgetAppState createState() => DynamicWidgetAppState();
}
class DynamicWidgetAppState extends State<DynamicWidgetApp>{
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
//Removing body:Center will change the reference
// and render the widget at the start of the page
child: Column(
mainAxisAlignment : MainAxisAlignment.center,
crossAxisAlignment : CrossAxisAlignment.center,
children: [
Text("My Centered Widget"),
]
),
),
floatingActionButton: FloatingActionButton(
// onPressed: ,
child : Icon(Icons.add),
),
);
}
}
For me the problem was there was was Expanded inside the column which I had to remove and it worked.
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded( // remove this
flex: 2,
child: Text("content here"),
),
],
)
You could use.
mainAxisAlignment:MainAxisAlignment.center
This will the material through the center in the column wise.
`crossAxisAlignment: CrossAxisAlignment.center'
This will align the items in the center in the row wise.
Container( alignment:Alignment.center, Child: Column () )
Simply use.
Center ( Child: Column () )
or rap with Padding widget . And adjust the Padding such the the column children are in the center.
You can also wrap the Column widget by Align.
Align(
alignment: Alignment.center,
child: Column(
children: [
Container(
width: 300,
margin: const EdgeInsets.fromLTRB(0, 70, 0, 0),
child: TextFormField(decoration: const InputDecoration(hintText: "First Name"))
),
...
]
)
)
Checkout this website for different ways of centering a widget: Link
In Addition, If you used
mainAxisAlignment: MainAxisAlignment.start
for centering all children but you still one of the children to be centered , Simply use Center() widget on the children.