Pass tap event to the child behind in Stack - flutter

After watching a tutorial video about creating a custom PageView in Flutter, I've come across a problem.
In order to achieve a custom PageView's page positions, I've used a stack of items that swipe left/right depending on the PageView that is placed in a Stack.fill() above every other element in the Stack.
Here's the example of the code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Home(),
);
}
}
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Test')),
body: Container(
height: 200.0,
child: Stack(
children: <Widget>[
Container(
color: Colors.blueGrey,
width: double.infinity,
height: double.infinity,
child: Material(
type: MaterialType.transparency,
child: InkWell(
onTap: () {
print('InkWell tapped');
},
),
),
),
Positioned.fill(
child: PageView.builder(
itemBuilder: (context, index) {
return Container(
child: Center(
child: Text(
'$index',
style: TextStyle(
color: Colors.white,
fontSize: 25.0,
),
),
),
);
},
itemCount: 4,
),
),
],
),
),
);
}
}
The problem is that I want to trigger a Tap event on the InkWell behind the PageView, while still be able to swipe the PageView.
Is it possible to pass the tap event to the element behind the PageView?
Thanks.
EDIT:
Since you should not extend widgets in flutter, I've just made a copy of Scrollable class that uses a HitTestBehavior.translucent instead of HitTestBehavior.opaque. After that you create a copy of PageView using this new Scrollable.
With such modification - the children behind the PageView start receiving gestures.

SOLUTION:
Since you should not extend widgets in flutter, I've just made a copy of Scrollable class that uses a HitTestBehavior.translucent instead of HitTestBehavior.opaque. After that you create a copy of PageView using this new Scrollable.
With such modification - the children behind the PageView start receiving gestures.

This one is without using a stack,
Scaffold(
appBar: AppBar(title: Text('Test')),
body: Container(
color: Colors.red,
height: 200.0,
child: InkWell(
onTap: () {
print('InkWell tapped');
},
child: PageView.builder(
itemCount: 4,
itemBuilder: (context, index) {
return Container(
child: Center(
child: Text(
'$index',
style: TextStyle(
color: Colors.white,
fontSize: 25.0,
),
),
),
);
},
),
),
),
),

Related

How can I wrap the height of the ListView.builder with a Container?

I have a screen using ListView.builder.
I gave the background of the list a light color with Container and I gaved BorderRadius.circular it.
Like here. I want this background to wrap the list. Like this.
Curves must to be at the beginning and end of the list.
I've tried:
I gave a physics: NeverScrollableScrollPhysics() to the ListView.builder. I wrapped the first Container with a SingleChildScrollView. Although background continues below I must to give a height. But I don't know how long the list will be. So that method did not work.
What am I missing?
Full code below
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
canvasColor: Colors.green,
primaryColor: Colors.green,
accentColor: Colors.white,
),
home: ListScreen(),
);
}
}
class ListScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Hello"),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22),
color: Colors.white38,
),
child: ListView.builder(
itemBuilder: (context, index) {
return Container(
child: Column(
children: [
InkWell(
onTap: () {},
child: Container(
height: MediaQuery.of(context).size.height / 7,
alignment: Alignment.center,
child: ListTile(
leading: Padding(
padding: const EdgeInsets.all(0),
child: Container(
child: ClipRRect(
borderRadius: BorderRadius.circular(6),
child: Image.network(
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/eb/Ash_Tree_-_geograph.org.uk_-_590710.jpg/330px-Ash_Tree_-_geograph.org.uk_-_590710.jpg",
fit: BoxFit.cover,
),
),
),
),
title: Text(
"${index + 1}. Tree",
),
),
),
),
Divider(
color: Colors.white,
height: 0,
),
],
),
);
},
itemCount: 12,
),
),
));
}
}
The way tried in the question was correct. It is only necessary to add shrinkWrap: true to the ListView.builder().
It will look like this:
SingleChildScrollView(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22),
color: Colors.white38,
),
child: ListView.builder(
itemBuilder: (context, index) {
return Container(//builder);
},
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: list.length,
),
),
),
The ListView widget is simply the stationary window on the screen that the child widgets scroll through. In order to achieve the desired effect, you need to use BorderRadius.only to round the correct corners of the first and last children of the ListView.
You are on the correct path here
I've tried: I gave a physics: NeverScrollableScrollPhysics() to the ListView.builder. I wrapped the first Container with a SingleChildScrollView. Although background continues below I must to give a height. But I don't know how long the list will be. So that method did not work.
in addition this this,
what you are missing here is using Expanded widget using expanding widget let the child expand as much as it want try this
Padding(
padding: const EdgeInsets.all(16),
child: Expanding(
child:Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(22),
color: Colors.white38,
),
child: ListView.builder(
// your ListView.builder code here with neverScrollablePhysics
),
),
))
remember to wrap this all a singleChildScrollView() and maybe use a Column() too

Scrollable Listview issue - Flutter

class StoryUI extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Stories'),
),
body: Column(
children: [
Text('Georgy'),
Storycard(),
],
),
),
);
}
}
class Storycard extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView.builder(
itemBuilder: (context, int index) {
return GestureDetector(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 8),
child: Card(
elevation: 10,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16)),
child: Column(
children: <Widget>[
ClipRRect(
child: Image.asset(
news[index].image,
),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16.0),
topRight: Radius.circular(16.0)),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(news[index].newsHeadline,
style: GoogleFonts.ubuntu(
fontSize: 17, fontWeight: FontWeight.bold)),
)
],
),
),
),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => NewsDetails(news: news[index])));
});
},
itemCount: news.length,
);
}
}
I am trying here to have a list of scrollable cards. I want space above the cards to add some text. SO I tried using column and have a text and Listview Bulider as children. But the emulator is showing blank screen.
When the StoryCard() is alone used as the body the output comes with widgets. I can't add anything above the Listview. Can someone help.
enter image description here
Use Expanded class or Flexible class for the ListView.
ListView requires a finite height for it's content.
With your current code, ListView is having infinite height, hence the error. Expanded or Flexible helps you to have the remaining space to be utilized for your child
Solution
class StoryUI extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Stories'),
),
body: Column(
children: [
Text('Georgy'),
// here is the change
Expanded(
child: Storycard()
)
]
)
)
);
}
}
Using Flexible
class StoryUI extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Stories'),
),
body: Column(
children: [
Text('Georgy'),
// here is the change
Flexxible(
fit: FlexFit.loose,
child: Storycard()
)
]
)
)
);
}
}

How to create a button in flutter that always sticks to the bottom of the page?

I want to achieve the following PhonePe UI in flutter. How can I make sure that the "Proceed" button always remains at the bottom position ?
Image
You can copy paste run full code below
You can use bottomSheet attribute of Scaffold
return Scaffold(
...
bottomSheet: Container(
width: MediaQuery.of(context).size.width,
child: RaisedButton(
child: Text('PROCEED'),
onPressed: () {},
),
),
);
working demo
full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: BottomSheetTestPage(),
);
}
}
class BottomSheetTestPage extends StatefulWidget {
#override
_BottomSheetPageState createState() => _BottomSheetPageState();
}
class _BottomSheetPageState extends State<BottomSheetTestPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('bottom sheet'),
),
body: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(index.toString()),
subtitle: Text("${index}"),
);
},
itemCount: 300,
),
bottomSheet: Container(
width: MediaQuery.of(context).size.width,
child: RaisedButton(
child: Text('PROCEED'),
onPressed: () {},
),
),
);
}
}
You will solve using Expanded widget.
Expanded(
child: Align(
alignment: FractionalOffset.bottomCenter,
child: MaterialButton(
onPressed: () => {},
child: Text(’Proceed'),
),
),
),
You can expand the first part of the layout
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(child: Text('FirstPart')),
InkWell(
onTap: () => print('pushed'),
child: Container(
width: 100,
height: 60,
alignment: Alignment.center,
color: Colors.black26,
child: Text('PROCEED'),
),
)
],
);
}
}
You can use stack like this
Stack(
children: <Widget>[
//your other widgets here
Align(
alignment: Alignment.bottomCenter,
child: yourButton,
)
],
),
a simple way to do this is using a Scaffold since the scaffold has a bottomNavigationBar attribute that takes a widget then you can simply pass to it a button like this
Widget build(BuildContext context) {
return Scaffold(
// will always be at the bottom
bottomNavigationBar: FlatButton(onPressed: (){},child: Text("Proceed"),),
body: Text("your body goes here"),
);
}

How to make list view in card flutter

I am a new flutter developer.I try to make listview to view a set of data that comes from the database.The list now works, but as follows:
Now it is not presented separately.I need to display every element in the card.An example of what I'm trying to do:
In this picture, each item on the card is separate and separated from the second.How I can do it?If anyone knows the solution please help me.
my code now like that:
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Expanded(
child: Card(
child :FutureBuilder<List<Flowerdata>>(
future: fetchFlowers(),
builder: (context, snapshot) {
if (!snapshot.hasData) return Center(
child: CircularProgressIndicator()
);
return ListView(
children: snapshot.data
.map((data) => Column(children: <Widget>[
GestureDetector(
onTap: ()=>{
getItemAndNavigate(data.id, context)
},
child: Row(
children: [
Container(
width: 100,
height: 100,
margin: EdgeInsets.fromLTRB(10, 0, 10, 0),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child:
Image.network(data.flowerImageURL,
width: 200, height: 100, fit: BoxFit.cover,))),
Flexible(child:
Text(data.flowerName,
style: TextStyle(fontSize: 18))
),
]),),
Divider(color: Colors.black),
],
))
.toList(),
);
},
)
),
),
]
)
);
}
You need to wrap your item's Column(not the FutureBuilder) in with Card
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: <Widget>[
Expanded(
child: FutureBuilder<List<Flowerdata>>(
future: fetchFlowers(),
builder: (context, snapshot) {
if (!snapshot.hasData)
return Center(child: CircularProgressIndicator());
return ListView(
children: snapshot.data
.map((data) => Card(
child: Column(
children: <Widget>[
GestureDetector(
onTap: () => {getItemAndNavigate(data.id, context)},
child: Row(children: [
Container(
width: 100,
height: 100,
margin: EdgeInsets.fromLTRB(10, 0, 10, 0),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Image.network(
data.flowerImageURL,
width: 200,
height: 100,
fit: BoxFit.cover,
))),
Flexible(
child: Text(data.flowerName,
style: TextStyle(fontSize: 18))),
]),
),
Divider(color: Colors.black),
],
),
))
.toList(),
);
},
),
),
]));
}
Setup
Start a new Flutter project. I'm calling mine flutter_listview.
Open main.dart and replace the code with the following:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'ListViews',
theme: ThemeData(
primarySwatch: Colors.teal,
),
home: Scaffold(
appBar: AppBar(title: Text('ListViews')),
body: BodyLayout(),
),
);
}
}
class BodyLayout extends StatelessWidget {
#override
Widget build(BuildContext context) {
return _myListView(context);
}
}
// replace this function with the code in the examples
Widget _myListView(BuildContext context) {
return ListView();
}
Note the _myListView() function at the end. You will be replacing this with the code in the examples below
Basic types of ListViews
Static ListView
If you have a short list of items that don't change, then you can use the default ListView constructor to make it. This is useful for making something like a settings menu page.
Replace _myListView() with the following:
Widget _myListView(BuildContext context) {
return ListView(
children: <Widget>[
ListTile(
title: Text('Sun'),
),
ListTile(
title: Text('Moon'),
),
ListTile(
title: Text('Star'),
),
],
);
}
Run the app and you should see the following image. (After this when refreshing, usually hot reload works fine, but I find at times I need to do a hot restart or even completely stop and restart the app.)
ListTile customization
The Flutter team designed the ListTile widget to handle the normal content that you would want in a list. This means that most of the time there is no need to define a custom layout. You can just use the default ListTile for each item in the list. When we made a ListView in the example above we only used the title option. But we can also show subtitles, images, and icons.
Replace _myListView() with the following
Widget _myListView(BuildContext context) {
return ListView(
children: <Widget>[
ListTile(
leading: Icon(Icons.wb_sunny),
title: Text('Sun'),
),
ListTile(
leading: Icon(Icons.brightness_3),
title: Text('Moon'),
),
ListTile(
leading: Icon(Icons.star),
title: Text('Star'),
),
],
);
}
The leading is for adding an icon or image at the start of the ListTile.
You can also add an icon at the end if you specify the trailing attribute.
ListTile(
leading: Icon(Icons.wb_sunny),
title: Text('Sun'),
trailing: Icon(Icons.keyboard_arrow_right),
)
The right arrow icon makes it look like the list items are clickable, but they aren't. Not yet. We will see how to add touch events in the next section. It's easy. (Hint: onTap )
Instead of icons, we can also use images. The recommended image option is to use a CircleAvatar widget.
Replace _myListView() with the following:
Widget _myListView(BuildContext context) {
return ListView(
children: <Widget>[
ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage('assets/sun.jpg'),
),
title: Text('Sun'),
),
ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage('assets/moon.jpg'),
),
title: Text('Moon'),
),
ListTile(
leading: CircleAvatar(
backgroundImage: AssetImage('assets/stars.jpg'),
),
title: Text('Star'),
),
],
);
}
If you want MASTERING FLUTTER LISTVIEWS enter link description here

How to create ink splash that extends beyond GridView image?

I'm trying to create a grid of images with a 'Share' button in the top-right corner of each image. The problem is the ink splash from the IconButton doesn't extend beyond the image border. Also, it seems that there is both an ink splash on the IconButton AND an ink splash in the InkWell - there should only be one splash on the button.
I know that the ink splash is rendered on the Material layer, but I cannot seem to get that layer beyond the bounds of the image. Can anyone suggest how to do this?
Here's a screenshot:
Here's the code:
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
_buildGrid() {
List<String> imgs = [
"https://upload.wikimedia.org/wikipedia/commons/thumb/4/4f/Three_Sisters_Sunset.jpg/1280px-Three_Sisters_Sunset.jpg",
"https://upload.wikimedia.org/wikipedia/commons/c/c0/Opera_House_and_ferry._Sydney.jpg",
"https://upload.wikimedia.org/wikipedia/commons/b/b0/State_Library_of_New_South_Wales_Reading_Room_2017.jpg",
"https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Sydney_Harbour_Bridge_from_Circular_Quay.jpg/2880px-Sydney_Harbour_Bridge_from_Circular_Quay.jpg",
"https://upload.wikimedia.org/wikipedia/commons/thumb/c/c0/Amptower_centerpoint.jpg/800px-Amptower_centerpoint.jpg",
"https://upload.wikimedia.org/wikipedia/commons/thumb/4/49/Summer_at_Manly_Beach.jpg/2560px-Summer_at_Manly_Beach.jpg",
"https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Glasshouse.JPG/2560px-Glasshouse.JPG"
];
return new GridView.extent(
primary: true,
padding: const EdgeInsets.all(20.0),
crossAxisSpacing: 20.0,
mainAxisSpacing: 20.0,
maxCrossAxisExtent: 200.0,
childAspectRatio: 210.0 / 130.0,
children: imgs.map((id) => _buildThumbnail(id)).toList(),
);
}
_buildThumbnail(String url) {
return new Container(
decoration: new BoxDecoration(
border: new Border.all(
color: Colors.black45,
width: 1.0,
),
),
child: new Stack(
fit: StackFit.expand,
children: <Widget>[
new Center(
child: new Icon(
Icons.image,
color: Colors.black45,
),
),
new Image.network(
url,
fit: BoxFit.cover,
key: new Key("thumnail-$url"),
gaplessPlayback: false,
),
new Material(
type: MaterialType.transparency,
child: new InkWell(
onTap: () => print("Open image: $url"),
child: new Align(
alignment: FractionalOffset.topRight,
child: new IconButton(
splashColor: Colors.red,
icon: new Icon(Icons.share),
onPressed: () => print("Share image: $url"),
color: Colors.black87),
),
),
),
],
),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Grid Splash',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new Scaffold(
appBar: new AppBar(
title: new Text('Grid Splash'),
),
body: _buildGrid(),
),
);
}
}
Use an InkResponse widget to encapsulate the button widget.
Edit: Don't forget the enableFeedback attribute.
new InkResponse(splashColor: someColor,
enableFeedback: true,
child: new IconButton(...)
)