Related
I'm working on simple quiz app with BLoC pattern. Tried to implement swipe feature for each quiz with PageView.Builder, here is my code
class _QuizScreenState extends State<QuizScreen> {
#override
void initState() {
context.read<QuizInfoBloc>().add(GetQuizStat("1"));
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
backgroundColor: kSecondaryColor,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
actions: [],
),
body: Container(
decoration: BoxDecoration(color: kSecondaryColor),
width: double.infinity,
height: double.infinity,
child: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 0),
child: Column(
children: [
Padding(
padding:
const EdgeInsets.symmetric(horizontal: kDefaultPadding / 2),
child: ProgressBar(),
),
Spacer(
flex: 1,
),
Padding(
padding:
const EdgeInsets.symmetric(horizontal: kDefaultPadding / 2),
child: BlocConsumer<QuizInfoBloc, QuizInfoState>(
listener: (context, state) {
// TODO: implement listener
},
builder: (context, state) {
if (state is QuizInfoLoaded) {
return PageView.builder(
itemCount: state.quiz.questions.length,
itemBuilder: (context, index) => QuestionCard(
question: state.quiz.questions[index]));
} else {
return Container(
height: 0,
width: 0,
);
}
},
),
),
Spacer(
flex: 4,
),
BottomButtons(),
SizedBox(
height: 20,
),
// AnswerExplanation(
// correctOrWrong: kGreenColor,
// ),
],
),
)),
),
);
}
}
I've tried with wrapping PageView.Builder with Expanded which was wrapped with Column. But still getting a different error
Padding(
padding:
const EdgeInsets.symmetric(horizontal: kDefaultPadding / 2),
child: BlocConsumer<QuizInfoBloc, QuizInfoState>(
listener: (context, state) {
// TODO: implement listener
},
builder: (context, state) {
if (state is QuizInfoLoaded) {
return Column(
children: [
Expanded(
child: PageView.builder(
itemCount: state.quiz.questions.length,
itemBuilder: (context, index) => QuestionCard(
question: state.quiz.questions[index])),
),
],
);
} else {
return Container(
height: 0,
width: 0,
);
}
},
),
),
RenderFlex children have non-zero flex but incoming height constraints
are unbounded.
QuestionCard
class _QuestionCardState extends State<QuestionCard> {
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(kDefaultPadding / 2),
child: Column(
children: [
Text(
this.widget.question.questionText,
style: Theme.of(context)
.textTheme
.headline5
?.copyWith(color: Colors.white, fontWeight: FontWeight.bold),
),
SizedBox(
height: 30,
),
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: this.widget.question.options.length,
itemBuilder: (BuildContext context, int index) {
return OptionUI(option: this.widget.question.options[index]);
})
],
),
);
}
}
Since you already have a kSecondaryColor as backgroundColor of your Scaffold, you don't need a Container as body of your Scaffold:
Scaffold(
extendBodyBehindAppBar: true,
backgroundColor: kSecondaryColor,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
actions: [],
),
body: SafeArea(
// child: ...
),
)
A SafeArea widget is meant to be used on the top-level widget tree:
SafeArea(
Scaffold(
extendBodyBehindAppBar: true,
backgroundColor: kSecondaryColor,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
actions: [],
),
body: Padding(
// child: ...
),
)
)
Wrap an Expanded into the Padding of your BlocConsumer:
Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: kDefaultPadding / 2),
child: ProgressBar(),
),
Spacer(flex: 1),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: kDefaultPadding / 2),
child: BlocConsumer<QuizInfoBloc, QuizInfoState>(
listener: (context, state) {
// TODO: implement listener
},
builder: (context, state) {
if (state is QuizInfoLoaded) {
return PageView.builder(
itemCount: state.quiz.questions.length,
itemBuilder: (context, index) =>
QuestionCard(question: state.quiz.questions[index]));
} else {
return Container(height: 0, width: 0);
}
},
),
),
),
Spacer(flex: 4),
BottomButtons(),
SizedBox(height: 20),
// AnswerExplanation(correctOrWrong: kGreenColor),
],
);
You didn't provide the ProgressBar, QuestionCard and BottomButtons widgets, so if the error persists you should check them, but I think these changes should suffice.
My UI use case is similar to an Instagram user profile.
I have multiple widget on top and an infinite ListView.builder bellow with item.
I would like to scroll everything like within a ScrollView but the scrollview can not contain an infinite list.
How can I handle this situation in Flutter ?
Using CustomScrollView is the Solution
Here i have done Basic Implementation for you :
class MainBody extends StatefulWidget {
MainBody({Key key}) : super(key: key);
#override
_MainBodyState createState() => _MainBodyState();
}
class _MainBodyState extends State<MainBody> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
child: Container(
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Container(
height: 100,
width: 100,
decoration: BoxDecoration(
shape: BoxShape.circle, color: Colors.green),
child: Center(child: Text("ProfileImage")),
),
flex: 1,
),
Expanded(flex: 2, child: Text("Profile Statistics"))
],
),
Text("Bio"),
Text("Some Buttons"),
Text("Story Highlights"),
])),
),
SliverToBoxAdapter(
child: Container(
height: 150,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 10,
itemBuilder: (context, index) {
return Container(
margin: const EdgeInsets.all(10),
height: 100,
width: 100,
decoration: BoxDecoration(
shape: BoxShape.circle, color: Colors.red),
child: Center(child: Text((index + 1).toString())),
);
})),
),
SliverAppBar(
centerTitle: false,
pinned: true,
flexibleSpace: DefaultTabController(
initialIndex: 0,
length: 2,
child: TabBar(tabs: [
Center(child: Text("Icon1")),
Center(child: Text("Icon2")),
]),
),
),
SliverPadding(
padding: const EdgeInsets.all(8),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => Container(
height: 30,
child: Center(child: Text("Hey" + index.toString()))),
childCount: 20)),
)
],
),
);
}
}
Futher Enhancements can be done
I want to create 2 list type data
first linear and other grid type list
now requirement is that both data list height will be dynamic means it should be expand when , new object is added.
and sorting scrollable along with all 4 widgets(sort, list, sort, grid).
like below image:
i have tried but height is static ,
i have used expanded but not giving result as i'm expecting.
code:
Container(
padding: EdgeInsets.all(10),
child: Container(
child: Column(
children: [
sort(context),//sort widget
createForlderView(context), //dynamic list widget
sort(context), //sort widget
_createGridView()// dynamic grid list widget
],
),
),
);
Widget sort(BuildContext context){
return Container(
// color: Colors.red,
height: 25,
width: MediaQuery.of(context).size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Folder"),
Container(
// color:Colors.green,
width: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Sort"),
// SizedBox(width:5),
InkWell(onTap: () {}, child: Icon(Icons.sort))
],
),
),
],
),
);
}
// list view:
Widget createForlderView(BuildContext context) {
final _width = MediaQuery.of(context).size.width;
final _height = MediaQuery.of(context).size.height;
return Expanded(
child: Container(
height: _height / 1.2,
child: ListView.builder(
// padding: ,
itemCount: directoryItems.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
leading: Container(
height: 30,
width: 30,
child: Icon(Icons.folder, color: Colors.brown),
),
title: Text(directoryItems[index]),
subtitle: Text("15 items"),
trailing:
IconButton(icon: Icon(Icons.more_vert), onPressed: () {}),
));
}),
),
);
}
// grid view:
Widget _createGridView() {
var mSize = MediaQuery.of(context).size;
/*24 is for notification bar on Android*/
final double itemHeight = (mSize.height - kToolbarHeight) / 2;
final double itemWidth = mSize.width / 2;
int gridItemCount =
Provider.of<DocumentProvider>(context).allDocuments.length;
return Expanded(
child: Container(
height: 100,
child: GridView.count(
key: animatedListKey,
scrollDirection: Axis.vertical, //default
reverse: false,
crossAxisCount: 2,
crossAxisSpacing: 4.0,
mainAxisSpacing: 4.0,
childAspectRatio: (itemWidth / itemHeight),
children: List.generate(gridItemCount, (index) {
return Center(
child: SelectCard(
index: index,
itemHeight: itemHeight,
itemWidth: itemWidth,
deletefun: () {
Navigator.pop(context);
DeleteDialog(
index: index,
dateTime:
Provider.of<DocumentProvider>(context, listen: false)
.allDocuments[index]
.dateTime);
},
),
);
}),
),
),
);
}
output screen:
I have found my solution,
used CustomScrollView along with it's slivers: SliverToBoxAdapter (for single widget),SliverFixedExtentList (for linear list) , and SliverGrid (for Grid list).
import 'package:flutter/material.dart';
class ExpandableList extends StatefulWidget {
final List<FolderModel> listData;
final List<FilesModel> gridListData;
const ExpandableList({Key key, this.listData, this.gridListData}) : super(key: key);
#override
_ExpandableListState createState() => _ExpandableListState();
}
class _ExpandableListState extends State<ExpandableList> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
buildAppBar(),
sorting(title: "folders", tralingTitle: "sort", onTap: (){}),
expandableListBuilder(),
sorting(title: "files", tralingTitle: "sort",onTap: (){}),
expandableGridList(),
],
)
);
}
buildAppBar(){
return SliverAppBar(
title: Text("Multi Expandable list example"),
centerTitle: true,
pinned: true,
);
}
expandableListBuilder(){
return SliverFixedExtentList(
itemExtent: 75.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Card(
child: ListTile(
leading: Container(
height: 30,
width: 30,
child: Icon(Icons.folder, color: Colors.brown),
),
title: Text(widget.listData[index].title),
subtitle: Text(widget.listData[index].subtitle),
trailing:
IconButton(icon: Icon(Icons.more_vert), onPressed: () {}),
));
},
childCount: widget.listData.length
),
);
}
expandableGridList(){
return SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Card(
elevation: 8,
child: Container(
alignment: Alignment.center,
height: 100,
width: 100,
child: Text("${widget.gridListData[index].title}${(index+1).toString()}"),
),
);
},
childCount: widget.gridListData.length,
),
);
}
sorting({String title,String tralingTitle, void Function() onTap}){
return SliverToBoxAdapter(
child: Container(
padding: const EdgeInsets.all(10),
height: 50,
width: MediaQuery.of(context).size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(title),
Container(
// color:Colors.green,
width: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(tralingTitle),
// SizedBox(width:5),
InkWell(
onTap:onTap,
child: Icon(Icons.sort))
],
),
),
],
),
),
);
}
}
result:
Is there's a way to create a lists of GridViews as the below image in one screen...
I have a some Screen as the below one:
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
var _showOnlyFavorites = false;
AnimationController animationController;
bool multiple = true;
#override
void initState() {
animationController = AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
super.initState();
}
Future<bool> getData() async {
await Future<dynamic>.delayed(const Duration(milliseconds: 0));
return true;
}
#override
void dispose() {
animationController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppTheme.white,
body: FutureBuilder<bool>(
future: getData(),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
} else {
return Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
appBar(),
Expanded(
child: FutureBuilder<bool>(
future: getData(),
builder:
(BuildContext context, AsyncSnapshot<bool> snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
} else {
return PropertiesGrid(_showOnlyFavorites);
}
},
),
),
],
),
);
}
},
),
);
}
Widget appBar() {
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:
Image.asset('assets/images/logo.png', fit: BoxFit.contain),
),
),
),
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(
multiple ? Icons.dashboard : Icons.view_agenda,
color: AppTheme.dark_grey,
),
onTap: () {
setState(() {
multiple = !multiple;
});
},
),
),
),
),
],
),
);
}
}
as I have a widget which have the GridView.builder as the below code:
import 'package:aradi_online_vtwo/providers/properties.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/properties.dart';
import './property_item.dart';
class PropertiesGrid extends StatelessWidget {
final bool showFavs;
PropertiesGrid(this.showFavs);
#override
Widget build(BuildContext context) {
final productsData = Provider.of<Properties>(context);
final products = showFavs ? productsData.favoriteItems : productsData.items;
return GridView.builder(
padding: const EdgeInsets.all(10.0),
itemCount: products.length,
itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
// builder: (c) => products[i],
value: products[i],
child: PropertyItem(
// products[i].id,
// products[i].title,
// products[i].imageUrl,
),
),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1,
childAspectRatio: 3 / 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
), scrollDirection: Axis.horizontal,
);
}
}
I tried to set the height of the grid by wrapping it with a Container and set the height of it as to add more grids but it doesn't work.
and here's my Grid Item widget code:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/property.dart';
class PropertyItem extends StatelessWidget {
#override
Widget build(BuildContext context) {
final property = Provider.of<Property>(context, listen: false);
return InkWell(
onTap: () => {},
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
elevation: 7,
margin: EdgeInsets.all(2),
child: Stack(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15),
),
// color: Colors.transparent,
child: Image.asset(
property.image,
fit: BoxFit.fill,
),
),
Positioned(
top: 8,
right: 8,
child: Consumer<Property>(
builder: (ctx, property, _) => IconButton(
icon: Icon(
property.isFavorite ? Icons.favorite : Icons.favorite_border,
),
color: Colors.red,
onPressed: () {
property.toggleFavoriteStatus();
},
),
),
),
Positioned(
right: 20,
top: 100,
child: Container(
width: 300,
color: Colors.black54,
padding: EdgeInsets.symmetric(
vertical: 5,
horizontal: 20,
),
child: Text(
property.title,
style: TextStyle(
fontSize: 20,
color: Colors.white,
),
softWrap: true,
overflow: TextOverflow.fade,
),
),
)
],
),
),
);
}
}
You'll need a column where each ListView or GridView is wrapped inside a SizedBox (if you have a specific height) and you also can use Expanded to take whatever available space (like the last one in the given example):
You can post the code below in dartpad.dev and see how it works:
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyApp(),
));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: [
SizedBox(
height: 100,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 10,
itemBuilder: (c, i) {
return Card(
child: Container(
height: 100,
width: 100,
child: Center(child: Text("$i")),
),
);
},
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Align(
child: Text("The Second List"),
alignment: Alignment.centerRight,
),
),
SizedBox(
height: 100,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 10,
itemBuilder: (c, i) {
return Card(
child: Container(
height: 100,
width: 100,
child: Center(child: Text("$i")),
),
);
},
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Align(
child: Text("The Third List"),
alignment: Alignment.centerRight,
),
),
Expanded(
//height: 200,
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 3 / 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
scrollDirection: Axis.vertical,
itemCount: 20,
itemBuilder: (c, i) {
return Card(
child: Container(
height: 100,
width: 100,
child: Center(child: Text("$i")),
),
);
},
),
),
],
),
);
}
}
I'm trying to create a horizontal list view with some card.
I want the list view to have height X and the cards to have height Y, I don't know why but the cards are getting the height of the list view.
This is what I have:
class FinanceApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SafeArea(
child: Container(
color: Color(0x180000),
child: Column(
children: <Widget>[
Header(),
SizedBox(
height: 20,
),
Expanded(
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(32.0),
topRight: Radius.circular(32.0),
),
),
child: Column(
children: <Widget>[
Container(
height: 250,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
CustomCard(),
CustomCard(),
],
),
),
],
),
),
),
],
),
),
),
);
}
}
EDIT: The only thing that kinda works for me, Is to wrap the card container in another container, use padding to get the size I want, but it does not seem like a great solution.
Check this out:
import 'package:flutter/material.dart';
class StackOverFlow extends StatefulWidget {
#override
_StackOverFlowState createState() => _StackOverFlowState();
}
class _StackOverFlowState extends State<StackOverFlow> {
#override
Widget build(BuildContext context) {
return Scaffold(
body:
Center(
child: Container(
color: Colors.blue,
height: 200.0,
width: double.infinity,
child: ListView.builder(
physics: BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.all(16.0),
itemCount: 100,
itemBuilder: _buildItem,
),
),
),
);
}
Widget _buildItem (BuildContext context, int index) {
return Center(
child: Card(
child: Text(index.toString()),
),
);
}
}
And for giving children same size consider wrapping the cards with a Container:
Widget _buildItem (BuildContext context, int index) {
return Center(
child: Container(
height: 100.0,
width: 100.0,
child: Card(
child: Center(child: Text(index.toString())),
),
),
);
}