Multiply gesture detector in flutter - flutter

I have two views one over another and want get touches in both views. But now get touches only second view.
How can I configure GestureDetector that all touches will be send both widgets
import 'package:flutter/material.dart';
class TestPage extends StatefulWidget {
TestPage();
#override
_TestPageState createState() => _TestPageState();
}
class _TestPageState extends State<TestPage> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
backgroundColor: Colors.blueAccent,
body: Stack(
children: [_renderFirstWidget(), _renderSecondWidget()],
),
);
}
Widget _renderFirstWidget() {
return Center(
child: GestureDetector(
onTap: () {
print('first');
},
child: Container(height: 150, width: 150, color: Colors.greenAccent)),
);
}
Widget _renderSecondWidget() {
return Center(
child: GestureDetector(
onTap: () {
print('second');
},
child: Container(
height: 350,
width: 350,
color: Colors.redAccent.withOpacity(0.3))));
}
}

Related

Can you programmatically put a Draggable inside a DragTarget?

This sounds a strange thing to ask! But I have two drag targets, and two draggables. I want them to start off one in each target. Then, when one is drag&dropped onto the second target, the second target's draggable needs to jump across to the first target.
I guess the problem resolves down to whether you can programmatically put a draggable inside a target. Does this look possible, or is a DragTarget not suitable?
Some example code - I can't start the draggables inside the dragtargets
import 'package:flutter/material.dart';
MyDraggable drag1 = new MyDraggable(Colors.red);
MyDraggable drag2 = new MyDraggable(Colors.green);
MyDragTarget target1 = new MyDragTarget();
MyDragTarget target2 = new MyDragTarget();
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(fontFamily: 'PressStart'),
home: MyHomeScreen(),
);
}
}
class MyHomeScreen extends StatefulWidget {
MyHomeScreen({Key key}) : super(key: key);
createState() => MyHomeScreenState();
}
class MyHomeScreenState extends State<MyHomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.red[100],
appBar: AppBar(
toolbarHeight: 70.0,
title: Center(child: Text('Swap the draggables')),
backgroundColor: Colors.pink,
),
body: Container(
color: Colors.yellow[200],
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [target1, drag1, drag2, target2],
// children: [target1, SizedBox(width: 100), target2],
),
),
Text('Q1: Can the Draggables be started in the DragTargets?'),
Text('Q2: If you drag from one target to the other, '
'can the second swap to the other target?'),
],
),
),
);
} // End build()
} // End class MyHomeScreenState
class MyDragTarget extends StatelessWidget {
bool dragAccepted = false;
Color acceptedColor;
#override
Widget build(BuildContext context) {
return Stack(children: [
Container(color: Colors.blue,height: 90.0,width: 90.0,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: DragTarget<Color>(
builder: (context, List<Color> acceptedData, rejectedData) {
if (dragAccepted) {
return MyDraggable(acceptedColor);
} else {
return Container(color: Colors.grey,height: 50,width: 50,);
}
},
onWillAccept: (aColor) {
acceptedColor = aColor;
return true;
},
onMove: (moveData) {},
onAccept: (aColor) { dragAccepted = true; },
onLeave: (data) {},
),
),
),
]);
} // Build
} // End Class MyDragTarget
class MyDraggable extends StatefulWidget {
MyDraggable(this.color);
final Color color;
#override
_MyDraggableState createState() => _MyDraggableState();
}
class _MyDraggableState extends State<MyDraggable> {
#override
Widget build(BuildContext context) {
return Draggable(
data: widget.color,
child: Container(color: widget.color, height: 50, width: 50),
feedback: Container(color: widget.color, height: 50, width: 50),
childWhenDragging:
Container(color: Colors.pink[100], height: 50, width: 50),
onDragStarted: () {},
onDragEnd: (dragDetails) {
setState(() {});
},
onDragCompleted: () {},
onDraggableCanceled: (velocity, offset) {},
);
}
}
For the default Draggable from Flutter SDK, there is no method provided that allowed us to modify the offset/ position of the Draggable displaying on the UI (all those infos are stored in DraggableDetails of a Draggable).
With that said, there are 2 options you can try out:
Create a custom Draggable class that allows for manipulating its position on UI programatically
Change the property of the children of Draggable widget based on the onWillAccept callback of the DragTarget class (the color of each Container in this case)

Change image onTap

I was trying to change the image every time onTap.
But somehow the image is only getting changed only once.
Please review this piece of code and mention where am I going wrong
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
#override
_DemoState createState() => _DemoState();
}
String imagePath = "images/img4.jpg";
class _DemoState extends State<Demo> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Center(
child: Container(
width: 100,
height: 100,
child: GestureDetector(
onTap: () {
setState(() {
imagePath = "images/tmhm.jpg";
});
},
child: CircleAvatar(
maxRadius: 20.0,
child: Image.asset(imagePath),
),
),
),
),
),
);
}
}
Place your imagePath in your State class(_DemoState)
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
#override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
String imagePath = "images/img4.jpg";
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Center(
child: Container(
width: 100,
height: 100,
child: GestureDetector(
onTap: () {
if(imagePath == "images/img4.jpg"){
imagePath = "images/tmhm.jpg";
}else{
imagePath = "images/img4.jpg";
}
setState(() {});
},
child: CircleAvatar(
maxRadius: 20.0,
child: Image.asset(imagePath),
),
),
),
),
),
);
}
}

How to call a method that is defined in the main class('_MyHomePageState()') from a onPressed of a Flat Button defined in a another class?

I have the main class _MyHomePageState() where the scaffold is defined for the homepage and I have defined very widget that will go into the scaffold in a new class. Now I have to call a function/method that is defined in the main class from the onPressed of the FlatButton that is in the main class.
The function that is in the main class triggers the BottomSheet, the code for the bottom sheet is written in a new dart file.
When I write the Flat button code inside the scaffold normally and call the function it does trigger the bottom sheet.
Here's the code snippet:
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
///This below is the function
void openBottomSheet() {
var sheetController = scaffoldKey.currentState
.showBottomSheet((context) => BottomSheetWidget());
sheetController.closed.then((value) {
print("Bottom Sheet Closed");
});
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
key: scaffoldKey,
appBar: AppBar(
title: Text("Hello,World"),
),
backgroundColor: Colors.grey[800],
body: Stack(children: <Widget>[
Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: Colors.blueGrey,
),
Column(
children: <Widget>[
TopMenu(),
ButtonClass(),
],
),
]),
);
}
}
Here is the button class:
class ButtonClass extends StatefulWidget {
_ButtonClassState createState() => _ButtonClassState();
}
class _ButtonClassState extends State<ButtonClass> {
#override
Widget build(BuildContext context) {
return Center(
child: Column(
children: <Widget>[
//Container(color: Colors.blue, child: Text("Hello,World")),
Container(
height: 50,
width:100,
margin: EdgeInsets.all(10.0),
child: FlatButton(
onPressed: (){
///And I am trying to call that function here, but is not working
_MyHomePageState().openBottomSheet();
},
child: Container(
color: Colors.red,
),
),
),
],
),
);
}
}
You can take a function parameter in your child Button Class and then pass the desired function to it from your parent class _MyHomePageState.
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
///This below is the function
void openBottomSheet() {
var sheetController = scaffoldKey.currentState
.showBottomSheet((context) => BottomSheetWidget());
sheetController.closed.then((value) {
print("Bottom Sheet Closed");
});
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
key: scaffoldKey,
appBar: AppBar(
title: Text("Hello,World"),
),
backgroundColor: Colors.grey[800],
body: Stack(children: <Widget>[
Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
color: Colors.blueGrey,
),
Column(
children: <Widget>[
TopMenu(),
ButtonClass(onPressed: ()=> openBottomSheet() ),
],
),
]),
);
}
}
class ButtonClass extends StatefulWidget {
Function onPressed;
ButtonClass({this.onPressed});
_ButtonClassState createState() => _ButtonClassState();
}
class _ButtonClassState extends State<ButtonClass> {
#override
Widget build(BuildContext context) {
return Center(
child: Column(
children: <Widget>[
//Container(color: Colors.blue, child: Text("Hello,World")),
Container(
height: 50,
width:100,
margin: EdgeInsets.all(10.0),
child: FlatButton(
onPressed: () => widget.onPressed,
child: Container(
color: Colors.red,
),
),
),
],
),
);
}
}
I do believe you have to pass that function from main page to button.. something like this
class ButtonClass extends StatefulWidget {
final Function onPress;
const ButtonClass({this.onPress});
//...
//...
child: FlatButton(
onPressed: onPress,
child: Container(
color: Colors.red,
),
),
),
],
),
);
}
}
and in your main class call it
ButtonClass(onPress: ()=>openBottomSheet())

animate show or hide widgets with flutter

i have something like this :
import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyWidgetState();
}
}
class _MyWidgetState extends State<MyWidget> {
bool loading = true;
#override
Widget build(BuildContext context) {
if(loading) {
return Container(
color: Theme.of(context).scaffoldBackgroundColor,
child: Center(
child: SizedBox(
width: 24,
height: 24,
child: GestureDetector(
onTap: _toggle,
child: CircularProgressIndicator(),
),
),
),
);
} else {
return Container(
child: Center(
child: GestureDetector(
onTap: _toggle,
child: Text("WELCOME"),
),
),
);
}
}
_toggle() {
setState(() {
loading = !loading;
});
}
}
my big problem with flutter is animating between toggling widgets
i want when _toggle called, loading widget fadeOut and after animation completed remove from screen and then show normal widget with fadeIn effect
how can i achieved to this ?
thanks
Correct way is using AnimatedSwitcher widget:
class MyWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyWidgetState();
}
}
class _MyWidgetState extends State<MyWidget> {
bool loading = true;
#override
Widget build(BuildContext context) {
return Scaffold(
body: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: loading ? Container(
key: Key("loading"),
color: Theme.of(context).scaffoldBackgroundColor,
child: Center(
child: SizedBox(
width: 24,
height: 24,
child: GestureDetector(
onTap: _toggle,
child: const CircularProgressIndicator(),
),
),
),
) : Container(
key: Key("normal"),
child: Center(
child: GestureDetector(
onTap: _toggle,
child: const Text("WELCOME"),
),
),
),
),
);
}
_toggle() {
setState(() {
loading = !loading;
});
}
}
note: you must give a key for children, in my example if you remove key animation not work
import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyWidgetState();
}
}
class _MyWidgetState extends State<MyWidget> {
bool loading = true;
#override
Widget build(BuildContext context) {
return Container(
child: Stack(
children: <Widget>[
Center(
child: GestureDetector(
onTap: _toggle,
child: Text("WELCOME"),
),
),
IgnorePointer(
ignoring: !loading,
child: AnimatedOpacity(
opacity: loading ? 1 : 0,
duration: Duration(milliseconds: 500),
child: Container(
color: Theme.of(context).scaffoldBackgroundColor,
child: Center(
child: SizedBox(
width: 24,
height: 24,
child: GestureDetector(
onTap: _toggle,
child: CircularProgressIndicator(),
),
),
),
),
),
)
],
),
);
}
_toggle() {
setState(() {
loading = !loading;
});
}
}

flutter's AutomaticKeepAliveClientMixin doesn't keep the page state after navigator.push

was testing AutomaticKeepAliveClientMixin and run into an issue,
page loses state after navigator.push
anyone knows this issue? any workarounds? be glad for any info, cheers
my goal is to keep the page state
steps to reproduce: open app click PageOne's push-button then go back swipe right and left and the page loses state
image
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(home: MyApp()));
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
initialIndex: 0,
length: 2,
child: Scaffold(
body: TabBarView(
children: <Widget>[Page1(), Page2()],
),
bottomNavigationBar: Material(
child: TabBar(
labelColor: Colors.black,
tabs: <Widget>[
Tab(
icon: Icon(Icons.check),
),
Tab(
icon: Icon(Icons.check),
),
],
),
),
),
),
);
}
}
class Page1 extends StatefulWidget {
#override
Page1State createState() {
return new Page1State();
}
}
class Page1State extends State<Page1> with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
Container(
height: 300,
color: Colors.orange,
),
Container(
height: 300,
color: Colors.pink,
),
Container(
height: 300,
color: Colors.yellow,
child: Center(
child: Container(height: 26,
child: MaterialButton(
color: Colors.blue,
child:
Text('clicking this and back then swipe => page loses state'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => PushedPage()),
);
}),
),
),
),
],
);
}
#override
bool get wantKeepAlive => true;
}
class Page2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(height: 300, color: Colors.orange);
}
}
class PushedPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
color: Colors.blue,
),
);
}
}
From the documentation on AutomaticKeepAliveClientMixin:
A mixin with convenience methods for clients of
[AutomaticKeepAlive]. Used with [State] subclasses.
Subclasses must implement [wantKeepAlive], and their [build]
methods must call super.build (the return value will always return
null, and should be ignored).
So in your code, before you return the ListView just call super.build:
Widget build(BuildContext context) {
super.build(context);
return ListView(...
}