render slivergrid using bloc in flutter - flutter

Here is the case :-
i have a Bloc named XYZBloc which loads data from network. i need to render these lists into sliverGrid.
the same list goes pretty well with sliverList.
Store.dart
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => _xyzBloc,
child: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle.light,
child: Container(
width: MediaQuery.of(context).copyWith().size.width,
height: MediaQuery.of(context).copyWith().size.height,
child: BlocBuilder<XyzBloc, XyzState>(
bloc: _xyzBloc,
builder: (context, state) {
if (state is XYZLoadingState) {
return buildLoading();
} else if (state is XYZErrorState) {
return buildErrorUi(state.message);
} else if (state is XYZLoadedState) {
return _buildPageView(state.lists, context);
} else {
return Container();
}
}),
),
),
);
}
_buildPageView(List<XYZModel> lists, BuildContext context) {
return SliverGrid(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
return Container(
padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
color: Colors.grey, height: 130.0, width: double.infinity),
Text(lists[index].name)
],
),
);
}, childCount: lists.length),
);
}
code to load sliverGrid data :-
Home.dart
#override
Widget build(BuildContext context) {
double cardWidth = MediaQuery.of(context).size.width / 3.3;
double cardHeight = MediaQuery.of(context).size.height / 3.6;
return Container(
color: AppTheme.nearlyWhite,
child: Scaffold(
backgroundColor: Colors.transparent,
body: Column(
children: <Widget>[
SizedBox(
height: MediaQuery.of(context).padding.top,
),
getAppBarUI(),
Expanded(
child: CustomScrollView(
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate([
Padding(
padding: EdgeInsets.all(15),
child: Container(
width: MediaQuery.of(context).size.width,
height: 150,
child: BannerItems(),
),
),
])),
SliverList(
delegate: SliverChildListDelegate(
[
Padding(
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
child: Container(
decoration: BoxDecoration(color: Colors.transparent),
height: 100,
child: Category(),
),
),
],
),
),
Stores(),
],
)),
],
),
),
);
}
i have tried all possible way to debug but my noob sense couldn't help me to understand how this sliverGrid works with network data.
any suggestion, reference will be highly appreciated.

Related

Horizontal viewport was given unbounded height while using PageView.Builder

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.

Vertical ListView inside ScrollView flutter

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

How to create expandable dynamic linear list and expandable grid list in a same widget?

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:

How to create multiple horizontal `GridView` in the same screen using flutter

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")),
),
);
},
),
),
],
),
);
}
}

Items in ListView get it's height

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())),
),
),
);
}