I want to add my ListView.seperated into Column, so that I can add CircularProgressIndicator below this ListvView when loading more items. I have used advices from How to add a ListView to a Column in Flutter? so that I made structure Column -> Expanded -> ListVie, but I got errors and list cannot be loaded:
======== Exception caught by rendering library =====================================================
RenderBox was not laid out: RenderRepaintBoundary#e8fad NEEDS-LAYOUT NEEDS-PAINT
'package:flutter/src/rendering/box.dart':
Failed assertion: line 1929 pos 12: 'hasSize'
The relevant error-causing widget was:
The relevant error-causing widget was:
Column file:///C://lib/ui/pages/home/page/HomePage.dart:125:16
When the exception was thrown, this was the stack:
#0 RenderFlex.performLayout.<anonymous closure> (package:flutter/src/rendering/flex.dart:926:9)
#1 RenderFlex.performLayout (package:flutter/src/rendering/flex.dart:929:6)
#2 RenderObject.layout (package:flutter/src/rendering/object.dart:1781:7)
#3 ChildLayoutHelper.layoutChild (package:flutter/src/rendering/layout_helper.dart:54:11)
#4 RenderFlex._computeSizes (package:flutter/src/rendering/flex.dart:829:43)
Column file:///C:/.../.../lib/ui/pages/home/page/HomePage.dart:125:16
Could you tell me what have I done wrong? This AdvertisementTile builds new widgets, but it is built onto another column. Maybe this is wrong?
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:.../ui/pages/home/page/AdvertisementCard.dart';
import 'package:.../ui/pages/home/page/model/AdvertisementList.dart';
import '../../SizedBox.dart';
import 'AdvertisementProdRepository.dart';
import 'BottomAppBar.dart';
import 'FAB.dart';
import 'model/AdvertisementList.dart';
class HomePage extends StatefulWidget {
final String jwt;
const HomePage(this.jwt);
#override
_HomePage createState() => _HomePage();
factory HomePage.fromBase64(String jwt) => HomePage(jwt);
}
class _HomePage extends State<HomePage> {
late final String jwt;
late Future<AdvertisementList> _listOfItems;
final searchTextController = TextEditingController();
#override
void initState() {
super.initState();
jwt = widget.jwt;
_listOfItems = AdvertisementProdRepository.fetchAdvertisements(1);
}
#override
Widget build(BuildContext context) => Scaffold(
body: Scaffold(
backgroundColor: const Color(0xFEF9F9FC),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked,
floatingActionButton: buildFAB(),
bottomNavigationBar: BuildBottomAppBar(),
body: Container(
padding: EdgeInsets.only(left: 25.0, right: 25, top: 25),
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
children: [
TextFormField(
controller: searchTextController,
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
hintText: 'Szukaj',
fillColor: Color(0xffeeeeee),
filled: true),
),
buildSizedBox(20.0),
Padding(
padding: const EdgeInsets.only(left: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'Najnowsze ogłoszenia',
style: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold),
textAlign: TextAlign.left,
),
],
),
),
buildSizedBox(10.0),
FutureBuilder<AdvertisementList>(
future: _listOfItems,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
return AdvertisementTile(advertisements: snapshot.data!);
}
},
),
],
),
),
),
);
}
class AdvertisementTile extends StatefulWidget {
final AdvertisementList advertisements;
AdvertisementTile({Key? key, required this.advertisements}) : super(key: key);
#override
State<StatefulWidget> createState() => AdvertisementTileState();
}
class AdvertisementTileState extends State<AdvertisementTile> {
AdvertisementLoadMoreStatus loadMoreStatus =
AdvertisementLoadMoreStatus.STABLE;
final ScrollController scrollController = new ScrollController();
late List<Advertisement> advertisements;
late int currentPageNumber;
bool _loading = false;
#override
void initState() {
advertisements = widget.advertisements.items;
currentPageNumber = widget.advertisements.pageNumber;
super.initState();
}
#override
void dispose() {
scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return NotificationListener(
onNotification: onNotification,
child: Column(
children: <Widget>[
Expanded(
child: ListView.separated(
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
controller: scrollController,
itemCount: advertisements.length,
physics: const AlwaysScrollableScrollPhysics(),
itemBuilder: (_, index) {
return AdvertisementCard(data: advertisements[index]);
},
separatorBuilder: (BuildContext context, int index) {
return SizedBox(
height: 10,
);
},
),
),
],
),
);
}
/*child: Padding(
padding: const EdgeInsets.only(bottom: 28.0),*/
bool onNotification(ScrollNotification notification) {
if (notification is ScrollUpdateNotification) {
if (scrollController.position.maxScrollExtent > scrollController.offset &&
scrollController.position.maxScrollExtent - scrollController.offset <=
50) {
if (loadMoreStatus == AdvertisementLoadMoreStatus.STABLE) {
loadMoreStatus = AdvertisementLoadMoreStatus.LOADING;
_loading = true;
AdvertisementProdRepository.fetchAdvertisements(currentPageNumber + 1)
.then((advertisementObject) {
currentPageNumber = advertisementObject.pageNumber;
loadMoreStatus = AdvertisementLoadMoreStatus.STABLE;
setState(() => advertisements.addAll(advertisementObject.items));
_loading = false;
});
}
}
}
return true;
}
}
enum AdvertisementLoadMoreStatus { LOADING, STABLE }
So the way a Column behaves is that when you give it 2 children, the flutter engine estimates the space that the single widget would cover, and then it would render those two widgets accordingly.
An Expanded widget tells flutter to take a widget and then cover as much space as that widget can take. In some cases, it makes sense to use an Expanded widget if the other widget is wrapped in a Container, this way flutter knows that you are referring to the remaining space.
ListView is a widget that takes all available spaces available.
So to fix your render issue you can put the Listview in a Container and give the Container a fixed height then flutter knows how to manage your screen and avoid the rendering issue.
If you eventually get any, you'll still have most of your UI, then you'll just have to tweak the height of your Container.
E.g
Container(
height: 400,
child: ListView.separated(
itemBuilder: (context, index) {
return AdvertisementCard(
data: advertisements[index]);
},
separatorBuilder: (context, index) {
return SizedBox(
height: 10,
);
},
itemCount: reports.length),
)
You should constrain the height of the Listview as the Column wants to get all the available height of its parent (likely Scaffold in your case). So, an unbounded-height-widget as a Listview will cause this kind of error.
Solution: wrap the Listview with a Container of some height.
Related
How to display users based on category? I have never done this. What is the logic behind this to implement
Here is my AllUsersProvider class:
class AllUsersProvider extends ChangeNotifier {
bool isLoading = false;
List<UsersData>? userData = [];
Future<void> getAllUsers(BuildContext context) async {
isLoading = true;
notifyListeners();
try {
bool isConnected = await NetworkHelper.checkInternet();
if (!isConnected) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Kindly Connect to internet")));
} else {
final response = await ApiService.getUsers();
isLoading = false;
notifyListeners();
if (response.statusCode == 200) {
final jsonResponse = json.decode(response.body);
if (jsonResponse.containsKey("success")) {
if (jsonResponse["success"]) {
UsersModel usersModel = UsersModel.fromJson(jsonResponse);
userData = usersModel.data;
notifyListeners();
}
}
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Something went wrong")));
}
}
} catch (error) {
print(error);
isLoading = false;
notifyListeners();
}
}
}
And here I want to display users based on category
class UsersCategory extends StatefulWidget {
final int categoryId;
final List<HomeCategory>? categories;
const UsersCategory({Key? key, required this.categoryId, required this.categories}) : super(key: key);
#override
State<UsersCategory> createState() => _UsersCategoryState();
}
class _UsersCategoryState extends State<UsersCategory> {
late int _categoryId;
#override
void initState() {
super.initState();
_categoryId = widget.categoryId;
Provider.of<AllUsersProvider>(context, listen: false).getAllUsers(context);
}
void _updateCategoryId(int newCategoryId) {
setState(() {
_categoryId = newCategoryId;
});
}
#override
Widget build(BuildContext context) {
print(widget.categoryId);
return Scaffold(
appBar: AppBar(
title: const Text("Users Category"),
),
body: Column(
children: [
const Text("Category"),
const SizedBox(height: 15),
SizedBox(
width: double.infinity,
height: 80,
child: ListView.builder(
physics: const BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
itemCount: widget.categories?.length ?? 0,
itemBuilder: (context, index) => GestureDetector(
onTap: () {
_updateCategoryId(widget.categories?[index].id ?? 0);
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
children: [
Container(
height: 50,
width: 50,
decoration: BoxDecoration(
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black.withOpacity(0.5),
blurRadius: 10,
offset: const Offset(0.0, 5),
)
],
borderRadius: BorderRadius.circular(10),
color: Colors.white,
),
child: Image.network(
widget.categories?[index].photo.toString() ?? "",
fit: BoxFit.contain,
),
),
const SizedBox(height: 5),
Text(widget.categories?[index].enName.toString() ?? ""),
],
),
),
),
),
),
const SizedBox(height: 15),
ShowUsersBasedOnCategory(categoryId: _categoryId)
],
),
);
}
}
class ShowUsersBasedOnCategory extends StatefulWidget {
final int categoryId;
const ShowUsersBasedOnCategory({Key? key, required this.categoryId}) : super(key: key);
#override
State<ShowUsersBasedOnCategory> createState() => _ShowUsersBasedOnCategoryState();
}
class _ShowUsersBasedOnCategoryState extends State<ShowUsersBasedOnCategory> {
#override
Widget build(BuildContext context) {
return Expanded(
child: Consumer<AllUsersProvider>(builder: (context, provider, _) {
List<UsersData>? users = provider.userData?.where((user) => user.categoryId == widget.categoryId).toList();
if (provider.isLoading) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (users == null || users.isEmpty) {
return const Center(
child: Text("No users found for this category"),
);
} else {
return GridView.builder(
itemCount: users.length,
shrinkWrap: true,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: 7 / 5,
crossAxisCount: 2,
mainAxisSpacing: 15,
),
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Container(
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(10),
),
child: Center(child: Text(users[index].firstName.toString())),
),
),
);
}
}),
);
}
}
When i click on category i also face this error
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope<AllUsersProvider?> 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: _InheritedProviderScope<AllUsersProvider?>
value: Instance of 'AllUsersProvider'
listening to value
The widget which was currently being built when the offending call was made was: Builder
When the exception was thrown, this was the stack:
#0 Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:4634:9)
#1 Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:4646:6)
#2 _InheritedProviderScopeElement.markNeedsNotifyDependents (package:provider/src/inherited_provider.dart:577:5)
#3 ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:381:24)
#4 AllUsersProvider.getAllUsers (package:chatgpt_pracrtce/providers/all_users_provider.dart:14:5)
#5 _UsersCategoryState.didChangeDependencies (package:chatgpt_pracrtce/screens/users/users_category.dart:50:59)
#6 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:5119:11)
#7 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4944:5)
... Normal element mounting (214 frames)
#221 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3953:16)
#222 MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6512:36)
#223 Element.updateChild (package:flutter/src/widgets/framework.dart:3682:18)
#224 RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:6041:32)
#225 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6537:17)
#226 Element.updateChild (package:flutter/src/widgets/framework.dart:3660:15)
#227 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4993:16)
#228 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5133:11)
An error you described in your question is comes because you've initialize your user array from provider during widget building process, for immediate widget or array filter updation you need to use value notifier. And make sure to avoid using setState cause it slower down your application it indirectly rebuild all widgets exists on class
Starts using value notifier to achiev this,
class ….{
ValueNotifier<int> varCategoryId = ValueNotifier(-1);
#override
void initState() {
super.initState();
varCategoryId.value = widget.categoryId;
varCategoryId.notifyListeners();
}
When you click on particular category set below code,
onTap: () {
varCategoryId.value = widget.categories?[index].id ?? 0;
varCategoryId.notifyListeners();
},
& for user update wrap code within ValueListenableBuilder,
Replace your line,
ShowUsersBasedOnCategory(categoryId: _categoryId)
with
ValueListenableBuilder(
valueListenable: varCategoryId,
builder: (context, _varCategoryId, child){
List<UsersData>? users = provider.userData?.where((user) => user.categoryId == _varCategoryId).toList();
return Expanded(
child: GridView.builder(
itemCount: users.length,
shrinkWrap: true,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: 7 / 5,
crossAxisCount: 2,
mainAxisSpacing: 15,
),
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Container(
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(10),
),
child: Center(child: Text(users[index].firstName.toString())),
),
),
),
);
}
),
I am trying to swap two widgets on button click OR is there any other way to do this?
When i set
SizedBox(
height: double.infinity,
width: double.infinity, )
it gives error: BoxConstraints forces an infinite height. it works when manually set height and width.
I tried using
Expanded and flexible it gives error like this
RenderBox was not laid out: RenderRepaintBoundary#8b1a5 relayoutBoundary=up14 NEEDS-PAINT
'package:flutter/src/rendering/box.dart':
Failed assertion: line 1785 pos 12: 'hasSize'
Code
class _MyHomePageState extends State<MyHomePage> {
TextEditingController _uri = TextEditingController();
bool flag=false;
bool swap = false;
int _index = 0;
#override
void initState() {
swap = widget.swap;
super.initState();
}
#override
Widget build(BuildContext context) {
Widget swapWidget;
if (!swap) {
swapWidget = Center(
child: Expanded(
// card height
child: PageView.builder(
itemCount: 2,
controller: PageController(viewportFraction: 0.95),
onPageChanged: (int index) => setState(() => _index = index),
itemBuilder: (_, i) {
return Transform.scale(
scale: i == _index ? 1 : 0.9,
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
child: Center(
child: Text(
"Card ${i + 1}",
style: TextStyle(fontSize: 32),
),
),
),
);
},
)
)
);
} else {
swapWidget =new Card(
child:Text('Nothing To show')
);
}
var swapTile = new ListTile(
title: swapWidget,
);
return new Scaffold(
appBar: AppBar( title: Text('My APP')),
body: new ListView(
children: <Widget>[
swapTile,
],
),
);
}
}
What i am doing Wrong?
Thankyou
I am not entirely sure what you are trying to do here, but Few things. PageView widget expands to the maximum allowed space by default, so you don't need the Expanded or Center widget around it.
Second, I don't know why you would want to wrap Card widget with ListTile. You certainly can, but that's not how ListTile and Card are designed.
Third, because PageView widget can expand, it needs some size constraints, so it cannot be put inside a infinitely large widget like ListView, so you can remove that, and here is what I have. I hope it is close to what you were looking for.
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool swap = true;
int _index = 0;
#override
Widget build(BuildContext context) {
Widget swapWidget;
if (swap) {
swapWidget = PageView.builder(
itemCount: 2,
controller: PageController(viewportFraction: 0.95),
itemBuilder: (_, i) {
return Transform.scale(
scale: i == _index ? 1 : 0.9,
child: Card(
elevation: 6,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20)),
child: Center(
child: Text(
"Card ${i + 1}",
style: TextStyle(fontSize: 32),
),
),
),
);
},
);
} else {
swapWidget = Card(child: Text('Nothing To show'));
}
return Scaffold(
body: swapWidget,
);
}
}
Data.length is 10.
For the 10th value, I'm getting out of range error, while my Item count is still>10. Why is this happening
import 'package:flutter/material.dart';
import 'dart:ui';
import 'package:momnts/landscape.dart';
import 'myCard.dart';
import 'data.dart';
class Homepage extends StatefulWidget {
#override
_HomepageState createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
var controller =PageController(initialPage: 0, viewportFraction: 0.8);
int currentPage = 0;
#override
void initState(){
controller.addListener((){
int next = controller.page.round();
if(currentPage!=next){
setState(() {
currentPage = next;
});
}
});
}
int totalLen = data.length+1;
#override
Widget build(BuildContext context) {
if (MediaQuery.of(context).orientation == Orientation.portrait) {
return Container(
color: Colors.white,
child: SafeArea(
child: Scaffold(
body: Container(
child: Column(
children: <Widget>[
Container(
height: 80.0,
child: Row(
children: <Widget>[
Expanded(
child: Text(
"Momnts",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 25.0,
fontFamily: "Pacifico",
),
),
),
],
),
),
Expanded(
child: PageView.builder(
itemCount: totalLen+1,
itemBuilder: (context,int currentIdx){
if(currentIdx==0)
return SimplePage();
else if(currentIdx<10)
return MyCard(id:currentIdx,text:data[currentIdx],image: AssetImage('images/S$currentIdx.jpg'),name:name[currentIdx]);
else
return MyCard(id:currentIdx,text:data[currentIdx],image: AssetImage('images/S$currentIdx.jpg'),name:name[currentIdx]);
},
scrollDirection: Axis.horizontal,
controller: controller,
),
),
],
))),
),
);
} else
return Landscape();
}
}
class SimplePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
child: Row(children: <Widget>[
Expanded(
child: Text("Yes",
style: TextStyle(fontSize: 30.0,),),
),
CircleAvatar(child: Icon(Icons.arrow_right),)
],)
);
}
}
This is the error shown
The following RangeError was thrown building:
I/flutter (16859): RangeError (index): Invalid value: Not in range 0..9, inclusive: 10
I/flutter (16859):
I/flutter (16859): When the exception was thrown, this was the stack:
I/flutter (16859): #0 List.[] (dart:core-patch/growable_array.dart:149:60)
I/flutter (16859): #1 _HomepageState.build.<anonymous closure> (package:momnts/homepage.dart:65:66)
I/flutter (16859): #2 SliverChildBuilderDelegate.build (package:flutter/src/widgets/sliver.dart:446:15)**
If your data.length is 10 then you are passing 12 as your item count for the builder, where I'd assume you should have 10 so data.length.
int totalLen = data.length+1; //10+1
...
itemCount: totalLen+1, //10+1+1
I'm learning flutter, and I'm trying to achieve a set of clickable cards, I successfully created the cards, however when I tried to use GestureDetector and wrap it up in a listview builder I get the following error
Vertical viewport was given unbounded height. Viewports expand in the
scrolling direction to fill their container.In this case, a vertical
viewport was given an unlimited amount of vertical space in which to
expand.
Please find the code below (task_card.dart):
import 'package:flutter/material.dart';
import 'product_detail.dart';
class TaskCard extends StatelessWidget {
final Map<String, dynamic> product;
final Function updateProduct;
final Function deleteProduct;
final int productIndex;
TaskCard(this.product, this.productIndex, this.updateProduct, this.deleteProduct);
#override
Widget build(BuildContext context) {
return ListView.builder(
//shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return GestureDetector(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {
return ProductDetail(
product: product[index],
productIndex: index,
updateProduct: updateProduct,
deleteProduct: deleteProduct,
);
}));
},
child: Card(
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 10.0, top: 5.0),
child: Text(
product['title'],
style: TextStyle(
fontSize: 22.0,
fontWeight: FontWeight.bold,
),
),
),
],
),
],
),
),
);
}
);
}
}
(task.dart)
import 'package:flutter/material.dart';
import 'task_card.dart';
class Tasks extends StatelessWidget {
final List<Map<String, dynamic>> products;
final Function updateProduct;
final Function deleteProduct;
Tasks(this.products, this.updateProduct, this.deleteProduct);
Widget _buildTaskCard() {
Widget taskCard = Center(
child: Text('No Products found'),
);
if (tasks.length > 0) {
taskCard = ListView.builder(
itemBuilder: (BuildContext context, int index) =>
TaskCard(products[index], index, updateProduct, deleteProduct),
itemCount: products.length,
);
}
return taskCard;
}
#override
Widget build(BuildContext context) {
return _buildTaskCard();
}
}
I've tried warping up my listview builder in a flexible widget and also using shrink wrap but non of them worked (shrink wrap crashed the application).
I'm trying to make the card clickable so that it navigates to another page.
any help is appreciated, thanks :)
Okay, so I guess I found a fix. I have added an unnecessary listview builder in the task_card.dart and called it through tasks.dart which already has a listview builder. Sharing my code for anyone who want to refer.
task.dart
import 'package:flutter/material.dart';
import 'task_card.dart';
class Tasks extends StatelessWidget {
final List<Map<String, dynamic>> products;
final Function updateProduct;
final Function deleteProduct;
Tasks(this.products, this.updateProduct, this.deleteProduct);
Widget _buildTaskCard() {
Widget taskCard = Center(
child: Text('No Products found'),
);
if (tasks.length > 0) {
taskCard = ListView.builder(
itemBuilder: (BuildContext context, int index) =>
TaskCard(products[index], index, updateProduct, deleteProduct),
itemCount: products.length,
);
}
return taskCard;
}
#override
Widget build(BuildContext context) {
return _buildTaskCard();
}
}
task_card.dart
import 'package:flutter/material.dart';
import 'product_detail.dart';
class TaskCard extends StatelessWidget {
final Map<String, dynamic> product;
final Function updateProduct;
final Function deleteProduct;
final int productIndex;
TaskCard(this.product, this.productIndex, this.updateProduct, this.deleteProduct);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {
return ProductDetail(
product: product,
productIndex: productIndex,
updateProduct: updateProduct,
deleteProduct: deleteProduct,
);
}));
},
child: Card(
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 10.0, top: 5.0),
child: Text(
product['title'],
style: TextStyle(
fontSize: 22.0,
fontWeight: FontWeight.bold,
),
),
),
],
),
],
),
),
);
}
}
I am trying to create a scrollView using CustomScrollView.
The effect that I need, is very similar to this one.
I need the SliverList to be stacked above the SliverAppbar, without the list taking the whole screen and hiding the SliverAppbar.
The reason I want to do this, is that i need to attach a persistent Positioned widget on top of that list, and it won't appear unless the list is stacked above the SliverAppbar.
Here's my code.
Step one:
Use ListView inside SliverAppBar widget. To make css overflow:hidden effect.
Step two:
Add controller to NestedScrollView and move the button on scrolling in a stack. Plus calculate where you want to stop button moving.
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
ScrollController scrollController;
final double expandedHight = 150.0;
#override
void initState() {
super.initState();
scrollController = new ScrollController();
scrollController.addListener(() => setState(() {}));
}
#override
void dispose() {
scrollController.dispose();
super.dispose();
}
double get top {
double res = expandedHight;
if (scrollController.hasClients) {
double offset = scrollController.offset;
if (offset < (res - kToolbarHeight)) {
res -= offset;
} else {
res = kToolbarHeight;
}
}
return res;
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: Stack(
children: [
NestedScrollView(
controller: scrollController,
headerSliverBuilder: (context, value) {
return [
SliverAppBar(
pinned: true,
expandedHeight: expandedHight,
flexibleSpace: ListView(
physics: const NeverScrollableScrollPhysics(),
children: [
AppBar(
title: Text('AfroJack'),
elevation: 0.0,
),
Container(
color: Colors.blue,
height: 100,
alignment: Alignment.center,
child: RaisedButton(
child: Text('folow'),
onPressed: () => print('folow pressed'),
),
),
],
),
),
];
},
body: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
itemCount: 80,
itemBuilder: (BuildContext context, int index) {
return Text(
'text_string'.toUpperCase(),
style: TextStyle(
color: Colors.white,
),
);
},
),
),
Positioned(
top: top,
width: MediaQuery.of(context).size.width,
child: Align(
child: RaisedButton(
onPressed: () => print('shuffle pressed'),
child: Text('Suffle'),
),
),
),
],
),
);
}
}