How to make grid view item screen width Flutter - flutter

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;
}
}

Related

Flutter Bottom overflowed by xx pixel

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()

Flutter: Make list scrollable

this is a typical question that might be considered as low quality but I have been on this for about two hours, and I am just trying to understand this piece of code better, so instead of just telling me how to fix, could you please also explain a bit what is happening. I am sure that for someone more experienced that me, should be very easy to spot.
I am trying to make a scrollable list, and draw each row of the list, and be able to click in each row item. But my app draws all the items but I am only able to see some of the items, as much as the screen allows, which means it is not scrollable.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Some App Page'),
),
body: ListView(
children: <Widget>[
Stack(
alignment: const Alignment(1.0, 1.0),
children: <Widget>[
TextField(
controller: cityController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(hintText: 'Enter city...'),
),
TextButton(
onPressed: () {
cityController.clear();
},
child: const Icon(Icons.clear),
),
],
),
ElevatedButton(
onPressed: () {
_futureTime = fetchTimes(int.parse(cityController.text));
if (cityController.text.isNotEmpty) {
setState(() {
cityController.clear(); // Clear value
}); // clear the textField
FocusScope.of(context)
.requestFocus(FocusNode()); // hide the keyboard
}
},
child: const Text('Get City', style: TextStyle(fontSize: 20)),
),
Column(
children: <Widget>[
Center(
child: FutureBuilder<Times>(
future: _futureTime,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const CircularProgressIndicator();
}
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return myTimeCard( date, index);
},
itemCount: data == null ? 0 : data.length,
);
},
),
),
],
),
],
),
);
}
Widget myTimeCard(String date, int index) {
return InkWell(
onTap: () {
// Navigate to the next page & pass data.
print("tapped, -> " + index.toString()); // for debugging purpose!
},
child: Stack(
children: <Widget>[
Opacity(
opacity: 1,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Text(
index.toString(),
style: const TextStyle(
color: Colors.black,
fontSize: 22.0,
fontWeight: FontWeight.bold),
),
),
],
)
],
)
],
),
);
}
You are using two ListView s nested inside each other. In such cases you may need to let the Flutter know which ListView is the primary one. So, there is a property called primary. Try to set primary to false for the inner Listview.
return ListView.builder(
primary: false,
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return myTimeCard( date, index);
},
itemCount: data == null ? 0 : data.length,
);
The code you shared does not compile because I do not have additional context, so I had to spend some time to be able to make it compile, please make sure to provide a compilable code in the future.
the problem you're facing is because the main ListView is taking control of the scroll, to see the effect try scrolling by holding the screen from the button Get City.
There are many ways to solve this problem, depending on your goal, do you want to make the whole screen scrollable, or just the data list
Way 1. Make the whole screen scrollable:
by keeping the control of the scroll in the main ListView, and making all the descending widgets non-scrollable, which in your case, by making the widget that wraps the data a Column instead of ListView:
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final TextEditingController cityController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Some App Page'),
),
body: ListView(
children: <Widget>[
Stack(
alignment: const Alignment(1.0, 1.0),
children: <Widget>[
TextField(
controller: cityController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(hintText: 'Enter city...'),
),
TextButton(
onPressed: () {
cityController.clear();
},
child: const Icon(Icons.clear),
),
],
),
ElevatedButton(
onPressed: () {
_futureTime = fetchTimes(int.parse(cityController.text));
if (cityController.text.isNotEmpty) {
setState(() {
cityController.clear(); // Clear value
}); // clear the textField
FocusScope.of(context)
.requestFocus(FocusNode()); // hide the keyboard
}
},
child: const Text('Get City', style: TextStyle(fontSize: 20)),
),
Column(
children: <Widget>[
Center(
child: FutureBuilder<Times>(
future: _futureTime,
builder: (context, snapshot) {
// if (!snapshot.hasData) {
// return const CircularProgressIndicator();
// }
final data =
// snapshot.data;
List.generate(50, (index) => index.toString());
return Column(
children: [
for (int i = 0; i < data.length; i++)
myTimeCard(data[i], i)
],
);
},
),
),
],
),
],
),
);
}
Widget myTimeCard(String date, int index) {
return InkWell(
onTap: () {
// Navigate to the next page & pass data.
print("tapped, -> " + index.toString()); // for debugging purpose!
},
child: Stack(
children: <Widget>[
Opacity(
opacity: 1,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Text(
index.toString(),
style: const TextStyle(
color: Colors.black,
fontSize: 22.0,
fontWeight: FontWeight.bold),
),
),
],
)
],
)
],
),
);
}
}
Way 2. make the non-data widgets non-scrollable, and keep the scroll control in the data widget:
can be done by converting the main ListView to a non-scrollable Widget (in your case Column), and wrapping the data list in Expanded widget, so it takes all the space it can have (for more info about Expanded):
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final TextEditingController cityController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Some App Page'),
),
body: Column(
children: <Widget>[
Stack(
alignment: const Alignment(1.0, 1.0),
children: <Widget>[
TextField(
controller: cityController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(hintText: 'Enter city...'),
),
TextButton(
onPressed: () {
cityController.clear();
},
child: const Icon(Icons.clear),
),
],
),
ElevatedButton(
onPressed: () {
_futureTime = fetchTimes(int.parse(cityController.text));
if (cityController.text.isNotEmpty) {
setState(() {
cityController.clear(); // Clear value
}); // clear the textField
FocusScope.of(context)
.requestFocus(FocusNode()); // hide the keyboard
}
},
child: const Text('Get City', style: TextStyle(fontSize: 20)),
),
FutureBuilder<Times>(
future: _futureTime,
builder: (context, snapshot) {
// if (!snapshot.hasData) {
// return const CircularProgressIndicator();
// }
final data =
// snapshot.data;
List.generate(50, (index) => index.toString());
return Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return myTimeCard(date, index);
},
itemCount: data == null ? 0 : data.length,
),
);
},
),
],
),
);
}
Widget myTimeCard(String date, int index) {
return InkWell(
onTap: () {
// Navigate to the next page & pass data.
print("tapped, -> " + index.toString()); // for debugging purpose!
},
child: Stack(
children: <Widget>[
Opacity(
opacity: 1,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: Text(
index.toString(),
style: const TextStyle(
color: Colors.black,
fontSize: 22.0,
fontWeight: FontWeight.bold),
),
),
],
)
],
)
],
),
);
}
}
The issue is coming because we have two scrollable ListView. While both of them are scrollable, while scrolling when the inner ListView it gets focused and parent become unfocus and scroll event only effect on inner ListView and you can't rollback to parent ListView, A simple solution will be using NeverScrollableScrollPhysics on inner
ListView.builder.
child: ListView.builder(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
singleChildScrollView(
child: ListView.builder(
sinkwrap:true,
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,)
)
Simple and Easy

When I use GridView widgets as a children of ListView, the scroll control is active for the ListView rendered area only & not from the GridView area

5. I am having an issue with scrolling. The outer container ListView
is scrolling fine, but when I grab the gridView elements the scroll effect doesn't work. What is it that I am missing here? (GIF below)
HomeScreen() has HomeBody() widget; HomeBody() has ListView, whose children are BuildSectionContainers() which have a services widgets as child.
class HomeScreem extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Stethoscope',
style: TextStyle(
color: Theme.of(context).accentColor,
fontSize: 24,
fontFamily: GoogleFonts.pacifico().fontFamily,
letterSpacing: 1.5),
),
centerTitle: true,
),
// APP BODY
body: HomeBody(),
);
}
}
class HomeBody extends StatelessWidget {
#override
Widget build(BuildContext context) {
final healthService = Provider.of<HealthServices>(context);
final service = healthService.services;
return ListView(
children: [
//
BuildSectionContainer(
healthService: healthService,
service: service,
sectionTitle: 'SERVICES',
child: Container(
height: 245,
child: BuildGeneralServicesGrid(
healthService: healthService,
service: service,
),
),
),
//
BuildSectionContainer(
healthService: healthService,
service: service,
sectionTitle: 'EMERGENCY SERVICES',
child: Container(
height: 245,
child: BuildEmergencyServicesGrid(
healthService: healthService,
service: service,
),
),
),
],
);
}
}
class BuildEmergencyServicesGrid extends StatelessWidget {
const BuildEmergencyServicesGrid({
Key? key,
required this.healthService,
required this.service,
}) : super(key: key);
final HealthServices healthService;
final List<HealthService> service;
#override
Widget build(BuildContext context) {
return GridView.builder(
itemCount: healthService.services.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 1,
mainAxisSpacing: 1,
mainAxisExtent: 120,
),
itemBuilder: (context, index) => GestureDetector(
child: Card(
color: Theme.of(context).primaryColor.withOpacity(0.1),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(8.0),
child: service[index].icon,
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
service[index].serviceName,
style: Theme.of(context).textTheme.bodyText2,
textAlign: TextAlign.center,
),
),
],
),
),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => AudioConsultation(),
));
},
),
);
}
}
The issue happens because, the gridview interferes with the listview scrolling. You can set the physics property of your Gridview to NeverScrollableScrollPhysics to fix the issue.
GridView.builder(
// Disable scrolling for grid
physics: NeverScrollableScrollPhysics(),
// Your remaining code
),

I am not able to make my app scrollble, despite of having SingleChildScrollView in Flutter

In my app, i have used BlogTile and CategoryTile widgets (which were made by myself) and I am using them in Contaniers/Columns. When I used SingleChildScrollView with CategoryTile, and made axis as horizontal, it was working fine. But as soon as i use it for BlogTile, it doen't work. I am not able to scroll in my app vertically. But when i try to scroll vertically by clicking on the part between CategoryTile and BlogTile, it works. But when i try to scroll by clicking from anyb other section of it, it doesn't work. Please someone help me
Check this code -
import 'package:flutter/material.dart';
import 'package:news_app/helper/data.dart';
import 'package:news_app/helper/news.dart';
import 'package:news_app/models/article_model.dart';
import 'package:news_app/models/category_models.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<CategoryModel> categories = new List<CategoryModel>();
List<ArticleModel> articles = new List<ArticleModel>();
bool loading = true;
#override
void initState() {
// TODO: implement initState
super.initState();
categories = getCategories();
getNews();
}
getNews() async {
News newsClass = News();
await newsClass.getNews();
articles = newsClass.news;
setState(() {
loading = false;
print('Done');
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Flutter',
style: TextStyle(
color: Colors.black,
),
),
Text(
'News',
style: TextStyle(
color: Colors.blue,
),
),
],
),
//elevation: 2.0,
),
body: loading
? Center(
child: Container(
child: CircularProgressIndicator(),
),
)
: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: SingleChildScrollView(
child: Container(
padding: EdgeInsets.only(top: 10.0),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
///Categories
Container(
padding: EdgeInsets.symmetric(horizontal: 16.0),
height: 70.0,
child: ListView.builder(
itemCount: categories.length,
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemBuilder: (context, index) {
return CategoryTile(
imageUrl: categories[index].imageUrl,
categoryName: categories[index].categoryName,
);
},
),
),
SizedBox(
height: 30.0,
),
///Blogs
SingleChildScrollView(
child: Container(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: articles.length,
itemBuilder: (context, index) {
return BlogTile(
imageUrl: articles[index].urlToImage,
title: articles[index].title,
desc: articles[index].description,
);
},
),
),
),
],
),
),
),
),
),
),
);
}
}
class CategoryTile extends StatelessWidget {
final imageUrl, categoryName;
CategoryTile({this.imageUrl, this.categoryName});
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {},
child: Container(
margin: EdgeInsets.only(right: 16.0),
child: Stack(
children: <Widget>[
ClipRRect(
borderRadius: BorderRadius.circular(6.0),
child: Image.network(
imageUrl,
width: 120.0,
height: 160.0,
fit: BoxFit.cover,
),
),
Container(
alignment: Alignment.center,
width: 120.0,
height: 160.0,
decoration: BoxDecoration(
color: Colors.black26,
borderRadius: BorderRadius.circular(6.0)),
child: Text(
categoryName,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w500,
fontSize: 14.0,
),
),
),
],
),
),
);
}
}
class BlogTile extends StatelessWidget {
final String imageUrl, title, desc;
BlogTile(
{#required this.imageUrl, #required this.desc, #required this.title});
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
Image.network(imageUrl),
Text(title),
Text(desc),
],
),
);
}
}
I think the issue here is that you're giving unbounded height and width to some of the ScrollViews.
First off, don't use multiple scrolling widgets nested inside one another. But if you want to do that, try wrapping each of your scrollview within a Container like this:
SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Container(
height: 50.0,
width: 50.0,
child: SingleChildScrollView(
child: ...,
),
),
),
What I'd also suggest is that instead of using a SingleChildScrollView, use a ListView widget. It works almost the same and you can put multiple children inside it. A simple ListView() will work. Don't use ListView.builder or any other aggregate function.

Flutter : how to hide and show button on last and first index in listView

i set two buttons(left and right Button) on top of ListView. buttons work for scrolling on click. now i want to hide the left button when index is 0 and the right button when index is last. more explain to clear, the left button will be hidden in first index and the right button will be hidden in last index. please help me.
class ScrollingLeftAndRightButtonHide extends StatefulWidget {
#override
_ScrolllingOnClickButtonState createState() =>
_ScrolllingOnClickButtonState();}
class _ScrolllingOnClickButtonState
extends State<ScrollingLeftAndRightButtonHide> {
final _controller = ScrollController();
var _width = 100.0;
#override
Widget build(BuildContext context) {
var sizeDevice = MediaQuery.of(context).size;
this._width = sizeDevice.width;
var recentIndexIncrease = 0;
var recentIndexDecrease = 0;
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Column(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
color: Colors.green,
)),
Expanded(
flex: 2,
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: ClipOval(
child: Material(
color: Colors.blue, // button color
child: InkWell(
splashColor: Colors.red, // inkwell color
child: SizedBox(
width: 56,
height: 56,
child: Icon(Icons.arrow_back)),
onTap: () {
var recentIndexDecreaseMinus =
recentIndexDecrease--;
_animateToIndex(recentIndexDecrease);
},
),
),
),
),
Expanded(
flex: 2,
child: Container(
color: Colors.transparent,
)),
Padding(
padding: const EdgeInsets.only(right: 8),
child: ClipOval(
child: Material(
color: Colors.blue, // button color
child: InkWell(
splashColor: Colors.red, // inkwell color
child: SizedBox(
width: 56,
height: 56,
child: Icon(Icons.arrow_forward)),
onTap: () {
_animateToIndex(recentIndexIncrease);
},
),
),
),
),
],
)),
Expanded(
flex: 16,
child: Container(
// height: 400,
child: ListView.builder(
controller: _controller,
scrollDirection: Axis.horizontal,
physics: PageScrollPhysics(),
itemCount: word_data.drink.length,
itemBuilder: (BuildContext context, int index) {
recentIndexIncrease = index;
recentIndexDecrease = index;
var wordDataReplace = word_data.drink[index]
.replaceAll(" ", "_")
.toLowerCase();
return Container(
child: Column(
children: <Widget>[
Expanded(
flex: 6,
child: GestureDetector(
child: Container(
color: Colors.purple,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Image.asset(
"asset/drink_images/" +
wordDataReplace +
".png",
fit: BoxFit.contain,
),
),
),
)),
],
),
width: sizeDevice.width,
);
}),
color: Colors.yellow,
),
),
],
),
),
);
}
_animateToIndex(i) => _controller.animateTo(_width * i,
duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn);
}
this image of (ListView with top two Button)
I think it might be easier for you to replace ListView.builder by a Flutter_Swiper it will make your life a lot easier. Or maybe you can add a listner to your ScrollController in the initState where it handles the value of two Boolean variables leftButtonEnabled and rightButtonEnabled and set them to true or false depending on the position of the Controller
EDIT :
here's an example of using Flutter swiper in your code, I tried to make it simple and in the same time adding multiple features that can help you ( like SwiperControl ) I hope it helps you a little bit.
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
home: ScrollingLeftAndRightButtonHide(),
),
);
}
class ScrollingLeftAndRightButtonHide extends StatefulWidget {
#override
_ScrolllingOnClickButtonState createState() =>
_ScrolllingOnClickButtonState();
}
class _ScrolllingOnClickButtonState
extends State<ScrollingLeftAndRightButtonHide> {
SwiperController _controller = SwiperController();
SwiperControl _control = SwiperControl(color: Colors.white);
double get _width => MediaQuery.of(context).size.width;
double get _height => MediaQuery.of(context).size.height;
bool inFirstPage = true;
bool inLastPage = false;
List<String> word_data = [
"First",
"Second",
"Third",
"Fourth",
"Fifth",
"Sixth",
"Last",
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Column(
children: <Widget>[
Container(
color: Colors.white,
child: Row(
children: <Widget>[
if (!inFirstPage)
IconButton(
color: Colors.indigoAccent,
onPressed: () {
_controller.previous();
},
icon: Icon(Icons.arrow_back),
),
Spacer(),
if (!inLastPage)
IconButton(
color: Colors.indigoAccent,
onPressed: () {
_controller.next();
},
icon: Icon(Icons.arrow_forward),
),
],
),
),
Expanded(
child: Container(
color: Colors.white,
child: Swiper(
controller: _controller,
control: _control,
loop: false,
scrollDirection: Axis.horizontal,
itemCount: word_data.length,
onIndexChanged: (value) {
if (value == word_data.length - 1)
setState(() {
inLastPage = true;
});
else if (value == 0)
setState(() {
inFirstPage = true;
});
else {
setState(() {
inFirstPage = false;
inLastPage = false;
});
}
},
itemBuilder: (BuildContext context, int index) {
return Container(
child: Column(
children: <Widget>[
Expanded(
child: GestureDetector(
child: Container(
width: _width,
alignment: Alignment.center,
color: Colors.indigoAccent,
child: Text(word_data[index]),
),
),
),
],
),
);
},
),
),
),
],
),
),
);
}
}
Add two variables in your state as
class _ScrolllingOnClickButtonState
extends State<ScrollingLeftAndRightButtonHide> {
bool leftEnabled = false; //this is initial visibility of left button
bool rightEnabled = true; //this is initial visibility of right button
........
Then in your build function where you are displaying left and right button add if statement
#override
Widget build(BuildContext context) {
var sizeDevice = MediaQuery.of(context).size;
this._width = sizeDevice.width;
var recentIndexIncrease = 0;
var recentIndexDecrease = 0;
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Column(
.............
if(leftEnabled)
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: ClipOval(
child: Material(
color: Colors.blue, // button color
child: InkWell(
splashColor: Colors.red, // inkwell color
child: SizedBox(
width: 56,
height: 56,
child: Icon(Icons.arrow_back)),
onTap: () {
var recentIndexDecreaseMinus =
recentIndexDecrease--;
_animateToIndex(recentIndexDecrease);
if (index == 0) { //Your logic to detect start of the list.
leftEnabled = false; //if it is the start make left invisible
}
if(list.size > 1)
rightEnabled = true; //whenever left button is pressed and your data has more than 1 element make right visible
setState(() {});
},
),
),
),
),
...............
Same code for the right button.
You cannot do it through your current structure of code. To achieve it you will have to move those arrow button Icons inside of the listView like this:
return ListView.builder(
scrollDirection: Axis.horizontal,
physics: PageScrollPhysics(),
itemCount: 5,
itemBuilder: (BuildContext context, int index) {
recentIndexIncrease = index;
recentIndexDecrease = index;
var wordDataReplace = word_data.drink[index].replaceAll(" ", "_").toLowerCase();
return Column(
children: <Widget>[
Row(
children: [
//Left arrow is the button indicating left arrow
if (index != 0) LeftArrow,
//Rightarrow is the button indicating right arrow
if (index != 4) RightArrow
],
),
Expanded(
child: GestureDetector(
child: Container(
color: Colors.purple,
padding: const EdgeInsets.all(10.0),
child: Image.asset("asset/drink_images/" +
wordDataReplace +
".png",
fit: BoxFit.contain,
),
),
)),
],
);
});