I am trying to construct a page with multiple widgets Row, Column, Expanded, ListView, etc...
I am a bit confused. I want a page scrollable with my widgets ThemeList.
I have the error :
A RenderFlex overflowed by 28 pixels on the bottom.
class SettingsViewState extends State<SettingsView> {
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
drawer: const NavDrawer(),
appBar: AppBar(
title: Text(AppLocalizations.of(context)!.settingsTitle),
backgroundColor: Theme.of(context).primaryColor,
),
body: CustomScrollView(slivers: [
SliverFillRemaining(
child: Column(
children: const [
ThemeList(),
SizedBox(height: 8),
ThemeList(),
SizedBox(height: 8),
ThemeList(),
],
),
),
]),
);
}
}
class ThemeList extends StatelessWidget {
const ThemeList({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.all(10),
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).primaryColor, width: 2),
borderRadius: BorderRadius.circular(10),
),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 5, left: 20),
child: Text(
AppLocalizations.of(context)!.settingsThemeSubTitle,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 23,
),
),
)
],
),
ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8),
itemCount: AppTheme.values.length,
itemBuilder: (context, index) {
final itemAppTheme = AppTheme.values[index];
var nameTheme = itemAppTheme.toString()
return Card(
color: appThemeData[itemAppTheme]?.primaryColor,
child: ListTile(
title: Text(
nameTheme,
style: appThemeData[itemAppTheme]?.textTheme.bodyText1,
),
onTap: () {
BlocProvider.of<ThemeBloc>(context).add(
ThemeChanged(theme: itemAppTheme),
);
Preferences.saveTheme(itemAppTheme);
},
),
);
},
)
],
),
),
),
);
}
}
Desired result :
Just wrap the ListView.builder() inside ThemeList with an Expanded and the problem would vanish.
If you want to have all the items inside each ThemeList displayed with a scroll for the whole screen then the easiest why is to do the following:
Change the CustomScrollView in the body of the Scaffold to be SingleChildScrollView with the Column as its child.
Remove the Expanded at the start of ThemeList.
Remove the ListView.builder() inside the ThemeList and replace it with any looping logic to directly render the cards, for example:
...AppTheme.values.map((itemAppTheme) {
var nameTheme = itemAppTheme.toString();
return Card(
color: appThemeData[itemAppTheme]?.primaryColor,
child: ListTile(
title: Text(
nameTheme,
style: appThemeData[itemAppTheme]?.textTheme.bodyText1,
),
onTap: () {
BlocProvider.of<ThemeBloc>(context).add(
ThemeChanged(theme: itemAppTheme),
);
Preferences.saveTheme(itemAppTheme);
},
),
);
}).toList()
Related
my case is that I am retrieving values images and text for challenges (like products ...etc), the challenges should appear one by one vertically first the image appears then the text appears over the image in the centre so I used stack and padding and I was able to retrieve one challenge information only, now I want to retrieve all challenges vertically using gridview builder, so I have did this :
Widget build(BuildContext context) {
return GridView.builder(
scrollDirection: Axis.vertical,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: _challenges.length),
itemBuilder: (_, index) {
return InkWell(
onTap: () {},
child: Stack(
children: [
Padding(
padding: EdgeInsets.all(10.0),
child: Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(15),
child: Image(
image:
NetworkImage(_challenges[index]["image-path"][0]),
fit: BoxFit.cover,
height: 150,
width: 350,
opacity: AlwaysStoppedAnimation(.4),
),
),
),
),
Padding(
padding: const EdgeInsets.all(60.0),
child: Center(
child: Text(
"${_challenges[index]["name"]}\n${_challenges[index]["date"]}",
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
),
),
],
),
);
});
every time i hot reload the app i get this error:
'package:flutter/src/rendering/sliver_grid.dart': Failed assertion: line 319 pos 15: 'crossAxisCount != null && crossAxisCount > 0': is not true.
and in just in case this is how i retrieved the data from Firestore:
List _challenges = [];
fetchChallengeData() async {
var _fireStoreInstance = FirebaseFirestore.instance;
QuerySnapshot qn = await _fireStoreInstance.collection("challenges").get();
setState(() {
for (int i = 0; i < qn.docs.length; i++) {
_challenges.add({
"image-path": qn.docs[i]["image-path"],
"name": qn.docs[i]["name"],
"date": qn.docs[i]["date"],
});
}
});
}
#override
void initState() {
fetchChallengeData();
super.initState();
}
the home screen where i use to display the element looks like:
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.purple,
title: Text(
"أتحداك",
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
),
centerTitle: true,
),
body: SingleChildScrollView(
child: Column(
children: [
AdsBanner(),
SizedBox(
height: 50,
),
Directionality(
textDirection: TextDirection.rtl,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text(
"التحديات",
style: TextStyle(fontSize: 20),
),
Text(
" (إضغط على التحدي للإشتراك به)",
style: TextStyle(fontSize: 15),
)
],
),
),
),
ChallengeCard(),
],
),
),
endDrawer: NavigationDrawer());
so basically the parent is a column and the parent of the column is singleChildScrollView,
any help I would be grateful, Thanks.
We are getting data from future fetchChallengeData, So it will be null initially, Try returning another widget on null or empty cases
Widget build(BuildContext context) {
return _challenges!=null && _challenges.isNotEmpty? GridView.builder(...): SizedBox.shrink();
Though _challenges.isNotEmpty enough while we've List _challenges = [];
I think we are seeking somthing like this
Widget myGridView() {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4, //number of items on single Row
),
itemCount: _challenges.length, // number of item will render
itemBuilder: (context, index) => Text("Your item Builder"),
);
}
Widget placement
body: Column(
children: [
AdsBanner(),
SizedBox(
height: 50,
),
Directionality(
textDirection: TextDirection.rtl,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Text(
"التحديات",
style: TextStyle(fontSize: 20),
),
Text(
" (إضغط على التحدي للإشتراك به)",
style: TextStyle(fontSize: 15),
)
],
),
),
),
Expanded(child: ChallengeCard()),
],
),
I'm trying to make a drawer widget that uses a ListViewBuilder to populate itself based on a list injected into the ViewModel.
However, I'm having issues getting it to play ball.
I've wrapped the LVB in a SizedBox to provide it with vertical bounds (since it was throwing a bunch of errors, as suggeested by another answer, and that's stopped those, but now I'm getting an overflow.
The header also doesn't fill out the width anymore either.
class MainDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<MainDrawerViewModel>(builder: (context, model, child) {
return Drawer(
child: Column(
children: [
DrawerHeader(
decoration: const BoxDecoration(color: ThemeColors.primaryDark),
child: Text(S.current.drawerTitle, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 30)),
),
SizedBox(
height: double.maxFinite,
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: model.mainDrawerItems.length,
itemBuilder: (_, index) {
final drawerItem = model.mainDrawerItems[index];
return ListTile(
leading: drawerItem.icon,
title: Text(drawerItem.title, style: Theme.of(context).textTheme.headline6),
selected: model.currentScreen == drawerItem.screen,
selectedTileColor: ThemeColors.selectedDrawerItem,
onTap: () {
model.selectScreen(drawerItem.screen);
Navigator.pop(context);
},
);
}),
),
],
));
});
}
}
This feels like something that should be pretty easy... What am I missing here?
Use Expanded widget on listView instead of height: double.maxFinite,
Expanded(
child: ListView.builder(
padding: EdgeInsets.zero,
double.maxFinite = 1.7976931348623157e+308; and it is equal to 1.7976931348623157 × 10^308 which is too big. and the overflow happens.
For header, you can wrap With SizedBox and provide width: double.maxFinite,. Also you can just use a container with decoration like
class MainDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: [
Container(
width: double.maxFinite,
height: 200, // based on your need
decoration: const BoxDecoration(color: Colors.amber),
padding: EdgeInsets.only(left: 16, top: 16),
child: Text(
"S.cu ",
style: TextStyle(
color: ui.Color.fromARGB(255, 203, 19, 19),
fontWeight: FontWeight.bold,
fontSize: 30),
),
),
Expanded(
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: 3,
itemBuilder: (_, index) {
return ListTile(
leading: Icon(Icons.abc_outlined),
title: Text("drawerItem.title",
style: Theme.of(context).textTheme.headline6),
selected: true,
onTap: () {},
);
}),
),
],
));
}
}
How to create a grid view where each item is in the center of the screen and width fits to given screen ? Whatever I do, in horizontal scroll my items are big and I can't scale them to the center of the screen. I want to scroll one item per screen.
I was trying ListView, Padding, Wrap, Align all the time the same.
What I have:
What I want:
My code:
class NewestView extends StatelessWidget {
const NewestView({Key? key, required this.state}) : super(key: key);
final TestState state;
#override
Widget build(context) {
return BlocBuilder<NavigationCubit, NavigationState>(
builder: (context, state) {
return Builder(builder: (context) {
return GridView.count(
padding: const EdgeInsets.all(25),
crossAxisCount: 1,
children: _generateGrid(15, context),
scrollDirection: Axis.horizontal,
shrinkWrap: true,
);
});
});
}
List<Widget> _generateGrid(int number, BuildContext context) {
List<Widget> gridResult = [];
for (int i = 0; i < number; i++) {
IconData genIcon = RandomIconData.randomIcon();
Image image = Image(image: Picsum().randomPic().image);
gridResult.add(
Builder(
builder: (BuildContext context) => InkWell(
borderRadius: BorderRadius.circular(50),
onTap: () {
_onTapAction(context, genIcon, image);
},
child: Card(
child: Column(
children: [
Flexible(
flex: 1
child: Center(
// color: Colors.deepOrangeAccent,
child: Text(
'Test',
style: Theme.of(context)
.textTheme
.caption
?.copyWith(fontWeight: FontWeight.bold),
),
),
),
Flexible(
flex: 4,
child: Builder(builder: (context) {
return Container(
color: Colors.transparent,
child: image,
);
}),
),
Flexible(
flex: 2,
child: Container(
color: Colors.transparent,
child: const Icon(
Icons.monetization_on,
color: Colors.amber,
),
),
)
],
),
),
),
),
);
}
return gridResult;
}
You essentially want to make a ListView. But you went with GridView with crossAxisCount = 1. GridView children have aspect ratio of 1:1 unless you say otherwise. So change the GridView to ListView. Give the Card a child with size relative to viewport using MediaQuery so it doesn't get squeezed look.
class NewestView extends StatelessWidget {
const NewestView({
Key? key,
}) : super(key: key);
// final TestState state;
#override
Widget build(context) {
return ListView(
children: _generateGrid(15, context),
scrollDirection: Axis.horizontal,
shrinkWrap: true,
);
}
List<Widget> _generateGrid(int number, BuildContext context) {
List<Widget> gridResult = [];
for (int i = 0; i < number; i++) {
IconData genIcon = Icons.ac_unit_outlined;
Image image = Image.network('https://via.placeholder.com/450x800.png');
gridResult.add(
Card(
child: SizedBox(
width: MediaQuery.of(context).size.width,
child: Column(
children: [
Flexible(
flex: 1,
child: Center(
// color: Colors.deepOrangeAccent,
child: Text(
'Test',
style: Theme.of(context)
.textTheme
.caption
?.copyWith(fontWeight: FontWeight.bold),
),
),
),
Flexible(
flex: 4,
child: Builder(builder: (context) {
return Container(
color: Colors.transparent,
child: image,
);
}),
),
Flexible(
flex: 2,
child: Container(
color: Colors.transparent,
child: const Icon(
Icons.monetization_on,
color: Colors.amber,
),
),
)
],
),
),
),
);
}
return gridResult;
}
}
I want to be able to make a section of the page scrollable, which will be between two sections of fixed sizes, but I want this section to expand as much as possible.
The reason why I want this, if the device is very small, the scroll works, but if the device is large, the scroll is not necessary.
I am trying to place a SingleChildScrollView (), inside a Flexible (), so that it expands as much as possible.
I am trying this but an error occurs
The design is like this
There are several ways to do it, you can do it with a ListView.builder
class UiMenu extends StatelessWidget {
const UiMenu({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
flex: 2,
child: Container(
color: Colors.blue,
),
),
Expanded(
flex: 8,
child: Container(
child: ListView.builder(
padding: EdgeInsets.all(0.0),
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return Card(
margin: EdgeInsets.only(bottom: 2),
child: ListTile(
leading: Icon(
Icons.equalizer_rounded,
size: 45,
),
title: Text(
"Option ${index + 1}",
style: Theme.of(context).textTheme.headline6,
),
trailing: Icon(
Icons.chevron_right,
size: 45,
),
),
);
},
),
),
),
Expanded(
flex: 1,
child: Container(
color: Colors.orange,
),
)
],
),
);
}
}
I want to get effect like this - when scrolled up, appbar is transparent with listview visible below it:
And scrolled down, only white color - first item below appbar:
My window layout:
return Container(
color: AppTheme.nearlyWhite,
child: SafeArea(
top: false,
bottom: false,
child: Scaffold(
backgroundColor: AppTheme.nearlyWhite,
body: Stack(
children: <Widget>[
DrawerUserController(
screenIndex: _drawerIndex,
drawerWidth: MediaQuery.of(context).size.width * 0.75,
animationController: (AnimationController animationController) => _sliderAnimationController = animationController,
onDrawerCall: (DrawerIndex drawerIndexdata) => _onDrawerCall(drawerIndexdata, _forceRefresh),
onDrawerTap:(DrawerIndex drawerIndexdata) => _onDrawerTap(drawerIndexdata),
screenView: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(8, MediaQuery.of(context).padding.top + 8, 8, 8),
child: _createAppBar(),
),
Expanded(
child:
Container(
color: Colors.white,
child: _screenView,
)
),
],
),
),
new FabDialer(_fabMiniMenuItemList, Colors.blue, new Icon(Icons.add))
],
),
),
),
);
}
_screenView is simple Listview().builder() and it shows InkWell widget for each item. My appbar is custom, defined like this:
_createAppBar() {
return SizedBox(
height: AppBar().preferredSize.height,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 8, left: 8),
child: Container(
width: AppBar().preferredSize.height - 8,
height: AppBar().preferredSize.height - 8,
),
),
Expanded(
child: Center(
child: Padding(
padding: const EdgeInsets.only(top: 4),
child: Column(
children: <Widget>[
Text(
_menuSelected,
style: TextStyle(
fontSize: 22,
color: AppTheme.darkText,
fontWeight: FontWeight.w400,
),
),
Text(
globals.cityName,
style: TextStyle(
fontSize: 15,
color: AppTheme.darkerText,
fontWeight: FontWeight.w400,
),
),
],
),
),
),
),
Padding(
padding: const EdgeInsets.only(top: 8, right: 8),
child: Container(
width: AppBar().preferredSize.height - 8,
height: AppBar().preferredSize.height - 8,
color: Colors.white,
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius:
BorderRadius.circular(AppBar().preferredSize.height),
child: Icon(Icons.refresh, color: AppTheme.dark_grey,),
onTap: () => setState(() => _forceRefresh = true),
),
),
),
),
],
),
);
}
That's how it looks now with first list item visible:
So, almost there, but when scrolled down, appbar won't be transparent:
I tried to mess around with setting my appbar backround to color with transparency, without success. Also I need to get my widgets actually overlapped (ListView needs to overlap my appbar) and it generates error messages from Flutter.
Any ideas how to do that properly?
set extendBodyBehindAppBar: true in Scaffold widget. Then use Opacity widget like this,
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: Home()));
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: PreferredSize(
preferredSize: const Size.fromHeight(kToolbarHeight),
child: Opacity( //Wrap your `AppBar`
opacity: 0.8,
child: AppBar(
title: Text("Demo"),
),
),
),
body: ListView.builder(
itemCount: 30,
itemBuilder: (context, index) {
return ListTile(
title: Text("Tile: $index"),
);
},
),
);
}
}
#override
Widget build(BuildContext context) {
return Container(
child: Stack(
children:[
Container(
color:Colors.white,
padding:EdgeInsets.all(10),
child:ListView.builder(
itemCount:25+1,
//length + 1 is beacause to show 1st item at the beginning
shrinkWrap:true,
itemBuilder:(con,ind){
return ind==0 ?
Container(height:70)
:ListTile(
title:Text('Item $ind',
style:TextStyle(color:Colors.black,))
);
}
)
),
Container(
height:70,
color:Colors.transparent,
child:Card(
color:Colors.white.withAlpha(80),
child: Row(
children:[
Expanded(
flex:1,
child: IconButton(
icon:Icon(Icons.list,color:Colors.black,size:25),
onPressed:(){
//todo
}
),
),
Expanded(
flex:3,
child: Text('Title',
style:TextStyle(color:Colors.black,)),
),
Expanded(
flex:1,
child: IconButton(
icon:Icon(Icons.search,color:Colors.black,size:25),
onPressed:(){
//todo
}
),
),
Expanded(
flex:1,
child: IconButton(
icon:Icon(Icons.more_vert,color:Colors.black,size:25),
onPressed:(){
//todo
}
),
)
]
),
)
)
]
)
);
}