I have a widget with a GestureDetector inside of Stack having Align as a child(where onTap is registered)
The layout code is,
body: Stack(
children: [
Padding(
padding: EdgeInsets.only(top: 28.0.vs),
child: PageView.builder(
controller: _pageController,
onPageChanged: _changeCurrentSlide,
itemCount: 2,
itemBuilder: (context, index) => Align(
alignment: Alignment.topCenter,
child: Container(
height: 700.h,
child: Card(
elevation: 20,
color: AppColors.black,
child: Container(
width: 350.w,
child: Stack(
overflow: Overflow.visible,
children: [
Text('Hai'),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () { print('hello'); },
child: Align(
alignment: Alignment(0, 1.1),
child: Material(
elevation: 20,
color: AppColors.transparent,
child: Container(
width: 70,
height: 40,
color: AppColors.goldenYellow,
child: Center(
child: Text(
'Next'
),
),
),
),
),
),
],
),
),
),
),
),
),
),
],
),
I need to call a function on tap of Align widget but only half the area of GestureDetector is working and on other half, onTap is not detected
Image for GestureDector, not working
This behavior occurs because the stack checks whether the pointer is inside its bounds before checking whether a child got hit:
If you notice the part of the button which is overflowing or not within the constraints of the Stack does not receive the tap. This is because the Stack widgets first checks whether the hit was inside its bounds before checking whether its child got tapped.
As a workaround you may wrap the parent widget of the Stack with Gesture detector and then check if the hit was within the bounds of your button or the intended widget. Please check the code below, I had to make some changes to your code in order to make my code work. Note if 'clipBehavior: Clip.none,' in my code gives you an error then you may comment it out and use 'overflow: Overflow.visible,' instead. Please check if this code works for you.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
final Color darkBlue = const Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(
MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: MyApp(),
),
);
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final List<GlobalKey> _key = <GlobalKey>[];
final PageController _controller = PageController();
int _currentPage = 0;
void onTapDown(
BuildContext context,
TapDownDetails details,
) {
final RenderBox box = context.findRenderObject();
final Offset localOffset = box.globalToLocal(details.globalPosition);
final RenderBox containerBox =
_key[_currentPage].currentContext.findRenderObject();
final Offset containerOffset = containerBox.globalToLocal(localOffset);
final onTap = containerBox.paintBounds.contains(containerOffset);
if (onTap) {
print("hello");
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTapDown: (TapDownDetails details) {
onTapDown(context, details);
},
child: Container(
width: 500,
height: MediaQuery.of(context).size.height,
color: Colors.white,
child: Stack(
children: [
Padding(
padding: EdgeInsets.only(
top: 28.0), // EdgeInsets.only(top: 28.0.vs),
child: PageView.builder(
controller: _controller, //_pageController,
onPageChanged: (val) {
_currentPage = val;
//_changeCurrentSlide();
},
itemCount: 2,
itemBuilder: (context, index) {
_key.add(GlobalKey());
return Align(
alignment: Alignment.topCenter,
child: Container(
height: 600, //700.h,
child: Card(
elevation: 20,
color: Colors.white, // AppColors.black,
child: Container(
width: 350, //350.w,
child: Stack(
//overflow: Overflow.visible,
clipBehavior: Clip.none,
children: [
Text('Hai'),
//GestureDetector(
// behavior: HitTestBehavior.opaque,
// onTap: () { print('hello'); },
// child:
Align(
alignment: Alignment(0, 1.1),
child: Material(
elevation: 20,
color: Colors
.transparent, // AppColors.transparent,
child: Container(
key: _key[index],
width: 70,
height: 40,
color: Colors
.black, //AppColors.goldenYellow,
child: Center(
child: Text('Next'),
),
),
),
),
//),
],
),
),
),
),
);
},
),
),
],
),
),
),
);
}
}
Related
I'm making this music app and when I click my image of the 3D Perspective PageView, the new page shows up well but the Curved Navigation Bar isn't showing up. I tired to find every resources on the internet but they are all saying about how to redirect with clicking the bottom nav bar.
This is my main.dart file
import 'package:flutter/material.dart';
import 'package:secondlife_mobile/bottomnavbar.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
//title: 'Twitch Player Example',
//title: 'Perspective PageView',
theme: ThemeData(
useMaterial3: true,
),
home: const BottomNavBar(),
);
}
}
And this is my home.dart file.
import 'package:flutter/material.dart';
import 'package:secondlife_mobile/PageViewHolder.dart';
import 'package:provider/provider.dart';
import 'package:secondlife_mobile/screens/artist_1.dart';
import 'package:secondlife_mobile/screens/artist_2.dart';
import 'package:secondlife_mobile/screens/artist_3.dart';
import 'package:secondlife_mobile/screens/artist_4.dart';
import 'package:url_launcher/url_launcher.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final PageStorageBucket bucket = PageStorageBucket();
late PageViewHolder holder;
late PageController _controller;
double fraction =
0.57; // By using this fraction, we're telling the PageView to show the 50% of the previous and the next page area along with the main page
Future<void> _launchURL(String url) async {
final Uri uri = Uri(scheme: "https", host: url);
if (!await launchUrl(
uri,
mode: LaunchMode.inAppWebView,
)) {
throw 'Can not launch url';
}
}
#override
void initState() {
super.initState();
holder = PageViewHolder(value: 2.0);
_controller = PageController(initialPage: 2, viewportFraction: fraction);
_controller.addListener(() {
holder.setValue(_controller.page);
});
}
int index = 1;
int currentIndex = 0;
final PageController controller = PageController();
List<String> images = [
"https://i.ytimg.com/vi/PWADVtWyE9Q/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDcneFqOxHd28mCncQxT3jOErmk9Q",
"https://i.ytimg.com/vi/djzDWMy1z7k/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCHwD_IA2ERzpZVxNvxCEOGr4fyTw",
"https://i.ytimg.com/vi/n8OxyKNBsuQ/hqdefault.jpg?sqp=-oaymwEcCOADEI4CSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAtW45_cxRqEWfUVw19UMts_9Q0lQ",
"https://i.ytimg.com/vi/7bDFD_WcU9I/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAsgAH6VRN4w0HKtVc528WA5QSZ2w",
"https://i.ytimg.com/vi/_ABk7TmjnVk/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAxCeIml0HUbjJ3igi1FFe1esdwdg",
"https://i.ytimg.com/vi/-8m0XFea2zE/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDBOBRGDJeDjhT1HbRobSN2Tp6hMA",
"https://i.ytimg.com/vi/mXLS2IzZSdg/hq720.jpg?sqp=-oaymwE2CNAFEJQDSFXyq4qpAygIARUAAIhCGAFwAcABBvABAfgB_gmAAtAFigIMCAAQARhdIFsoZTAP&rs=AOn4CLDS13MjaIBxjjhccIktpAb0azBG9g",
"https://i.ytimg.com/vi/HuzlYAMwwJY/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCmfMS9RENZuIJMQ8k2cf6MbHIpug",
"https://i.ytimg.com/vi/-nt_u4vo-DI/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAgUinltWhU-qqmgc_JroDLPt3OEg",
"https://i.ytimg.com/vi/tqtZIyN_Alg/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLD4woxvyiNXgmSile7PLz7uoRPQOQ",
];
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: const Color.fromARGB(255, 223, 234, 244),
appBar: AppBar(
backgroundColor: Colors.transparent,
centerTitle: true,
title: const Text('AppBar'),
),
body: SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 30,
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Playlist for you',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w400,
),
),
),
const SizedBox(height: 15),
Container(
child: Center(
child: AspectRatio(
aspectRatio: 1,
child: ChangeNotifierProvider<PageViewHolder>.value(
value: holder,
child: PageView.builder(
controller: _controller,
itemCount: 4,
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
if (index == 0) {
return InkWell(
//you should use InkWell for onTap or thing like that!!!
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const FirstArtist(),
),
);
},
child: MyPage(
number: index.toDouble(),
fraction: fraction,
),
);
}
if (index == 1) {
return InkWell(
//you should use InkWell for onTap or thing like that!!!
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const SecondArtist(),
),
);
},
child: MyPage(
number: index.toDouble(),
fraction: fraction,
),
);
}
if (index == 2) {
return InkWell(
//you should use InkWell for onTap or thing like that!!!
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ThirdArtist(),
),
);
},
child: MyPage(
number: index.toDouble(),
fraction: fraction,
),
);
}
if (index == 3) {
return InkWell(
//you should use InkWell for onTap or thing like that!!!
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const FourthArtist(),
),
);
},
child: MyPage(
number: index.toDouble(),
fraction: fraction,
),
);
} else {
return const Text("Can't find anything");
}
},
),
),
),
),
),
Transform.translate(
offset: const Offset(0, -85),
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Watch videos',
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
),
),
),
),
//https://www.youtube.com/watch?v=7a_RXHOkJLM
//https://github.com/Programmer9211/Flutter-Carousel-Slider/blob/main/lib/main.dart
Transform.translate(
offset: const Offset(0, -65),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
alignment: Alignment.center,
child: SizedBox(
height: 162,
width: 335,
child: PageView.builder(
controller: controller,
onPageChanged: (index) {
setState(() {
currentIndex = index % images.length;
});
},
itemBuilder: (context, index) {
return InkWell(
//you should use InkWell for onTap or thing like that!!!
onTap: () {
print("Tapped watch Videos Imageeeee");
},
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 35),
child: SizedBox(
height: 100,
width: 400,
child: Image.network(
images[index % images.length],
fit: BoxFit.fill,
),
),
),
);
},
),
),
),
],
),
),
////Your Playlist of the week text
Transform.translate(
offset: const Offset(0, -30),
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 35),
child: Text(
'Playlist of the week',
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w600,
),
),
),
),
Transform.translate(
offset: const Offset(0, -15),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 35),
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: 150,
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 180.0,
width: 220.0,
child: Image.asset(
'assets/images/album1.jpg',
height: 180.0,
width: 220.0,
),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 180.0,
width: 220.0,
child: Image.asset(
'assets/images/album2.jpg',
height: 180.0,
width: 220.0,
),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album3.jpg',
height: 160.0,
width: 200.0,
),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album4.jpg',
height: 160.0,
width: 200.0,
),
),
),
const SizedBox(
width: 30,
),
GestureDetector(
onTap: () {
_launchURL("www.google.com");
},
child: SizedBox(
height: 160.0,
width: 200.0,
child: Image.asset(
'assets/images/album5.jpg',
height: 160.0,
width: 200.0,
),
),
),
],
),
),
],
),
),
),
),
],
),
),
),
),
);
}
}
class MyPage extends StatelessWidget {
final number;
final double? fraction;
const MyPage({super.key, this.number, this.fraction});
#override
Widget build(BuildContext context) {
double? value = Provider.of<PageViewHolder>(context).value;
double diff = (number - value);
// diff is negative = left page
// diff is 0 = current page
// diff is positive = next page
//Matrix for Elements
final Matrix4 pvMatrix = Matrix4.identity()
..setEntry(3, 2, 1 / 0.9) //Increasing Scale by 90%
..setEntry(1, 1, fraction!) //Changing Scale Along Y Axis
..setEntry(3, 0, 0.004 * -diff); //Changing Perspective Along X Axis
final Matrix4 shadowMatrix = Matrix4.identity()
..setEntry(3, 3, 1 / 1.6) //Increasing Scale by 60%
..setEntry(3, 1, -0.004) //Changing Scale Along Y Axis
..setEntry(3, 0, 0.002 * diff) //Changing Perspective along X Axis
..rotateX(1.309); //Rotating Shadow along X Axis
return Stack(
fit: StackFit.expand,
alignment: FractionalOffset.center,
children: [
Transform.translate(
offset: const Offset(0.0, -47.5),
child: Transform(
transform: pvMatrix,
alignment: FractionalOffset.center,
child: Container(
decoration: BoxDecoration(boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
blurRadius: 11.0,
spreadRadius: 4.0,
offset: const Offset(
13.0, 35.0), // shadow direction: bottom right
)
]),
child: Image.asset(
"assets/images/image_${number.toInt() + 1}.jpg",
fit: BoxFit.fill),
),
),
),
],
);
}
}
There is a package called persistent_bottom_nav_bar 5.0.2.
Step1: Create a new StatefulWidget called PersistentNavBar that contains the CurvedNavigationBar widget:
Step2: Wrap your entire app with a PageView and set its children to the pages you want to navigate to. For each page, you can set its bottomNavigationBar to the PersistentNavBar widget.
I have a stateful widget called AddPhotosView that contains all the logic i am trying to achieve, i am using this package photo_manager to display images in the user's devices in a GridView.builder and i am trying to use image_cropper to be able to crop the image the user selects in the from the grid view.
This is the full code of my stateful widget :
class AddPhotosView extends StatefulWidget {
#override
State<AddPhotosView> createState() => _AddPhotosView();
}
class _AddPhotosView extends State<AddPhotosView> {
List<Widget> _mediaList = [];
int currentPage = 0;
int lastPage;
#override
void initState() {
super.initState();
_fetchNewMedia();
}
_handleScrollEvent(ScrollNotification scroll) {
if (scroll.metrics.pixels / scroll.metrics.maxScrollExtent > 0.33) {
if (currentPage != lastPage) {
_fetchNewMedia();
}
}
}
_fetchNewMedia() async {
var result = await PhotoManager.requestPermissionExtend();
if (result != null) {
// success
//load the album list
List<AssetPathEntity> albums =
await PhotoManager.getAssetPathList(onlyAll: true);
print(albums);
List<AssetEntity> media =
await albums[0].getAssetListPaged(page: currentPage, size: 60);
print(media);
List<Widget> temp = [];
for (var asset in media) {
temp.add(
FutureBuilder(
future: asset.thumbnailDataWithSize(
ThumbnailSize(200, 200),
),
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.done)
return GestureDetector(
onTap: () async {
File file = await asset.file;
print(file.toString());
},
child: Stack(
children: <Widget>[
Positioned.fill(
child: Image.memory(
snapshot.data,
fit: BoxFit.cover,
),
),
if (asset.type == AssetType.video)
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(right: 5, bottom: 5),
child: Icon(
Icons.videocam,
color: Colors.white,
),
),
),
],
),
);
return Container();
},
),
);
}
setState(() {
_mediaList.addAll(temp);
currentPage++;
});
} else {
// fail
PhotoManager.openSetting();
}
}
#override
Widget build(BuildContext context) {
// final controller = Get.put(EServicesController());
return Scaffold(
appBar: AppBar(
toolbarHeight: 50,
backgroundColor: Colors.white,
title: Column(
children: [
Text(
"Add Photos".tr,
style: GoogleFonts.poppins(
color: Color(0xff000000),
fontSize: 16,
fontWeight: FontWeight.w600),
),
],
),
centerTitle: false,
elevation: 0.5,
automaticallyImplyLeading: false,
leadingWidth: 15,
leading: new IconButton(
icon: new Icon(Icons.arrow_back_ios, color: Color(0xff3498DB)),
onPressed: () => {Get.back()},
),
),
body: Column(
children: [
Container(
color: Colors.white,
child: Padding(
padding: const EdgeInsets.only(
left: 45.0, right: 45, top: 22, bottom: 35),
child: Container(
height: 280,
width: 280,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
),
child: Stack(
fit: StackFit.expand,
children: [
Image.asset(
'assets/icon/image-test.png',
fit: BoxFit.cover,
),
ColorFiltered(
colorFilter: ColorFilter.mode(
Colors.white.withOpacity(0.3),
BlendMode.srcOut), // This one will create the magic
child: Stack(
fit: StackFit.expand,
children: [
Container(
decoration: BoxDecoration(
color: Colors.black,
backgroundBlendMode: BlendMode
.dstOut), // This one will handle background + difference out
),
Align(
alignment: Alignment.topCenter,
child: Container(
margin: const EdgeInsets.only(top: 80),
height: 200,
width: 200,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(100),
),
),
),
],
),
),
],
),
),
),
),
Expanded(
child: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scroll) {
_handleScrollEvent(scroll);
return;
},
child: Padding(
padding: const EdgeInsets.all(22.0),
child: GridView.builder(
itemCount: _mediaList.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
itemBuilder: (BuildContext context, int index) {
return _mediaList[index];
}),
),
),
),
],
),
//bottomNavigationBar: SendUpdateButton(),
);
}
}
My photo_manager package is working and displaying my images correctly, but i want to more with it.
Want i am trying to achieve :
Get the image path of an image selected by the user with the photo_manager package.
I also want to be able select multiple images at once or a single image when tapped from the grid view.
Then render the image selected by the user in the Container carrying the image.asset() widget.
Crop the image using the flutter package image_cropper.
Getting the file path of an image selected by the user is very important as i believe it can help me achieve many other logic.
I have tried getting the image path by wrapping the container carrying the image.asset with
GestureDetector(
onTap: () async {
File file = await asset.file;
print(file.toString());
},
But it don't to work :(
Please i would be entirely grateful if someone help me out with what i am trying to achieve as i have been struggling to achieve this for sometime now in my project.
NB: I am using the package: photo_manager because it helps me to customise the UI of where the user's gallery can be displayed, other packages has an out-of-the-box UI which i don't want. So i will be open to using another package for only cropping the image if possible.
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.
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,
),
),
)),
],
);
});
I have a home screen where at the top I declared a listview, with scrolldirection taking information from a list.dart file. This horizontal scrolling screen brings me 5 images and a text in each of them. I would like to insert an onpress directing to other screens according to the information passed in this list. Example: Chat, direct to chat.screen.
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Container(
width: double.infinity,
height: MediaQuery.of(context).size.height * 4 / 7,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xff40dedf), Color(0xff0fb2ea)],
),
),
),
Positioned(
top: 100,
left: 20,
child: Container(
height: 100,
width: MediaQuery.of(context).size.width,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: categoryData.length,
itemBuilder: (context, index) {
bool isSelected = true;
if (index == 0) {
isSelected = true;
}
Navigator.push<dynamic>(
context,
MaterialPageRoute<dynamic>(
builder: (BuildContext
context) =>
HomeList[index].navigateScreen,
),
);
return Row(
children: <Widget>[
Column(
children: <Widget>[
Container(
width: 65,
height: 65,
decoration: BoxDecoration(
color: isSelected
? Colors.transparent
: Colors.transparent,
borderRadius:
BorderRadius.circular(16),
border: Border.all(
color: Colors.white,
width: 1,
),
boxShadow: isSelected
? [
BoxShadow(
color: Color(0x14000000),
blurRadius: 10)
]
: null),
child: Center(
child: Image.asset(categoryData[index].imageUrl),
),
),
SizedBox(
height: 10,
),
Text(
categoryData[index].name,
style: TextStyle(color: Colors.white, fontSize: 15),
),
],
),
SizedBox(
width: 20,
)
],
);
},
),
),
),
Homelist
import 'package:flutter/material.dart';
import 'package:projeto/pages/chat.screen.dart';
class HomeList {
HomeList({
this.navigateScreen,
this.imagePath = '',
});
Widget navigateScreen;
String imagePath;
static List<HomeList> homeList = [
HomeList(
imagePath: 'assets/page1/usuario.png',
navigateScreen: ChatScreen(),
),
HomeList(
imagePath: 'assets/page1/entregas.png',
navigateScreen: ChatScreen(),
),
HomeList(
imagePath: 'assets/page1/msg.png',
navigateScreen: ChatScreen(),
),
HomeList(
imagePath: 'assets/page1/configurações.png',
navigateScreen: ChatScreen(),
),
HomeList(
imagePath: 'assets/page1/sair.png',
navigateScreen: ChatScreen(),
),
];
}
from what i understand, you want to transform the data in your HomeList file to a listview where clicking on one of its items takes you to its related page, you could use a ListView.builder with an itemBuilder and itemCount, the code below shows how you can achieve a listView where items are images with text in them and an onTap function:
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return GestureDetector(
child: Stack(
children: <Widget>[
Image.asset(
homeList[index].imagePath,
),
Positioned(child: Text())
],
),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => homeList[index].navigateScreen),
),
);
},
itemCount: homeList.length,
),
);
}
}