GestureDetector doesn't work when i use ListView.builder with Transform - flutter

I'm trying to position the containers in the center of the screen using Transform and I want GestureDetector to work on each Container, but, in this case, when I click on one of these containers it doesn't work, it's like there's something on top of it and that doesn't allow onTap to work.
When my Matrix4 looks like this GestureDetector works:
Matrix4(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
)..rotateZ(_rotate[index].value),
But when I add 100 to Matrix4 it doesn't work:
Matrix4(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 100, 0, 1,
)..rotateZ(_rotate[index].value),
Full code:
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
class TransformPage extends StatefulWidget {
#override
_TransformState createState() => _TransformState();
}
class _TransformState extends State<TransformPage>
with TickerProviderStateMixin {
//Variables
AnimationController _animationController;
List<Color> _color = [Colors.red, Colors.blue, Colors.indigo, Colors.green];
List<Animation> _rotate = List<Animation>(4),
_sizeWidth = List<Animation>(4),
_sizeHeight = List<Animation>(4);
Animation _curve;
#override
void initState() {
super.initState();
_animationController =
AnimationController(vsync: this, duration: Duration(seconds: 2));
_curve = CurvedAnimation(parent: _animationController, curve: Curves.ease);
for (int i = 0; i < 4; i++) {
_sizeWidth[i] = Tween<double>(begin: 0, end: 120).animate(_curve);
_sizeHeight[i] = Tween<double>(begin: 0, end: 120).animate(_curve);
_rotate[i] = Tween<double>(begin: 0, end: 2.35).animate(_curve);
}
_animationController.addListener(() {
setState(() {});
});
_animationController.forward();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CupertinoNavigationBar(
middle: Text("Testing"),
backgroundColor: Colors.grey.shade200,
),
body: ListView.builder(
itemCount: 4,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
print(index);
},
behavior: HitTestBehavior.translucent,
child: Stack(
alignment: Alignment.center,
children: [
Transform(
transform:
Matrix4(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 100, 0, 1,
)..rotateZ(_rotate[index].value),
child: Container(
width: _sizeWidth[index].value,
height: _sizeHeight[index].value,
color: _color[index],
),
alignment: Alignment.center,
),
],
),
);
},
),
);
}
}
I already tried using Positioned, but the result is the same :(

the Transform widget, has a special way to deal with his child, It changes its content the way you want it to be, but its reference remains on the main widget, I had the same thing when I used Transform.scale.
If you want the ListView widget to move away from the top edge, use the Padding widget,
it is worked, I copied your code to my project and Show debug paint, The result was like this :
with this code :
Matrix4(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 100, 0, 1,
)..rotateZ(_rotate[index].value),
the result is :
As for this code:
Matrix4(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
)..rotateZ(_rotate[index].value),
the result is :
the result with Padding widget is:
Note: onTap It actually works in the middle square

Related

Flutter User ColorFiltered to dark image with matrix

I want in flutter to user ColorFiltered, so i can make jpg image dark here is my code but i didn't get the right result, i don't understand matrix and how to change color this code was from internet, please any idea will be welcome.
ColorFiltered(
child: ColorFiltered(
child: Image.asset(
'myimage.jpg',
fit: BoxFit.fill,
),
colorFilter: ColorFilter.matrix(
[
//R G B A Const
-1, 0, 0, 0, 190, //
0, -1, 0, 0, 255, //
0, 0, -1, 0, 255, //
0, 0, 0, 1, 0, //
],
),
),
colorFilter: ColorFilter.mode(
Colors.white,
BlendMode.darken,
),
)
If you just want to make your image darker the better solution is probably just putting a transparent black Container over your image with a Stack widget:
Stack(children: <Widget>[
Image.network('https://picsum.photos/250?image=9'),
Positioned.fill(
child: Opacity(
opacity: 0.5,
child: Container(
color: const Color(0xFF000000),
),
),
),
]),
If you really want to use ColorFiltered let me know but you can do something like this:
ColorFiltered(
child: Image.network('https://picsum.photos/250?image=9'),
colorFilter: const ColorFilter.mode(
Color(0xFF3D3D3D),
BlendMode.darken,
),
),
Keep in mind that ColorFiltered will not just add a new grey layer over the image. It transforms every pixel of the image separately.
As per the documentation:
This widget applies a function independently to each pixel of child's content, according to the ColorFilter specified.
there is the result based on my search and a little refactoring :
static ColorFilter brightnessAdjust(double value) {
if (value >= 0.0) {
value = value * 255;
} else {
value = value * 100;
}
List<double> matrix;
if (value == 0) {
matrix = identityMatrix;
} else {
matrix = [
1,
0,
0,
0,
value,
0,
1,
0,
0,
value,
0,
0,
1,
0,
value,
0,
0,
0,
1,
0
];
}
return ColorFilter.matrix(matrix);
}
positive value to lighten and negative value to darken (-1 to 1), zero no change anything

Build a hall plan in Flutter. What is the best solution?

I want to make an interactive hall plan in Flutter, but I can't figure out how to implement it correctly.
Key points:
the plan should scale without loss of quality.
the view of the place should change slightly when you click
The first option: is to make it as a Canvas of a large size, and put it in the InteractiveViewer, but I do not know if it is possible to change the already drawn parts on tap?
The second option: is to make each place as an SVG with onTap event, and put it all in a Stack widget. And then again in InteractiveViewer. But there will be about 500 seats on the plan, and I'm not sure if the phone can handle scaling such a scene?
Maybe there are other solutions? I will be very grateful for the advice, I still have very little experience in Flutter.
This can be achieved with InteractiveViewer & GridView:
for any shape of place, you can draw it using 2d array, like
List<List<int>> triangleHall = [
[0, 0, 1, 0, 0],
[0, 1, 1, 1, 0],
[1, 1, 1, 1, 1],
]
List<List<int>> arcHall = [
[0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
]
List<Widget> _generateHallItem(List<List<int>> hall){
List<Widget> list = [];
hall.map((row) {
row.map((item) {
item == 0 ? list.add(Container(width:5, height:5)) : list.add(Container(width:5, height:5, color: Colors.red));
});
});
return list;
}
Wrap(
children: _generateHallItem,
OR
InteractiveViewer(
minScale: 0.8,
maxScale: 2.0,
child: GridView(),
),
try InteractiveViewer and CustomMultiChildLayout as a child (notice how easy you can animate the seats inside CinemaDelegate with passed AnimationController):
class CinemaHall extends StatefulWidget {
const CinemaHall({Key? key}) : super(key: key);
#override
_CinemaHallState createState() => _CinemaHallState();
}
class _CinemaHallState extends State<CinemaHall> with TickerProviderStateMixin {
late List<Offset> positions;
late List<ValueNotifier<bool>> states;
late AnimationController ctrl;
#override
void initState() {
super.initState();
positions = [
...List.generate(8, (index) => (Offset(index + 1, 0))),
for (int r = 1; r < 7; r++)
...List.generate(10, (index) => (Offset(index.toDouble(), r.toDouble()))),
];
states = List.generate(positions.length, (index) => ValueNotifier(false));
ctrl = AnimationController(vsync: this, duration: const Duration(milliseconds: 1000));
Future.delayed(const Duration(milliseconds: 2000), () => ctrl.forward());
}
#override
Widget build(BuildContext context) {
return ColoredBox(
color: const Color(0xff000044),
child: InteractiveViewer(
minScale: 1.0,
maxScale: 3.0,
child: CustomMultiChildLayout(
delegate: CinemaHallDelegate(ctrl, positions, const Size(64, 64)),
children: List.generate(positions.length, (index) => LayoutId(
id: index,
child: FittedBox(
child: ValueListenableBuilder<bool>(
valueListenable: states[index],
builder: (context, state, child) {
return TweenAnimationBuilder<double>(
duration: const Duration(milliseconds: 500),
tween: Tween(end: state? 0.8 : 0.2),
builder: (context, t, child) {
return IconButton(
icon: Icon(index == 43? Icons.people : Icons.person, color: Colors.white.withOpacity(t)),
padding: EdgeInsets.zero,
iconSize: 64,
tooltip: 'seat ${positions[index].dy.toInt() + 1}, ${positions[index].dx.toInt() + 1}',
onPressed: () {
states[index].value = !state;
print(index);
},
);
}
);
}
),
)
)),
),
),
);
}
#override
void dispose() {
super.dispose();
ctrl.dispose();
}
}
class CinemaHallDelegate extends MultiChildLayoutDelegate {
final AnimationController ctrl;
final List<Offset> positions;
final Size seatSize;
late Size cinemaHallSize;
CinemaHallDelegate(this.ctrl, this.positions, this.seatSize) : super(relayout: ctrl) {
double cols = positions.map((o) => o.dx).reduce(max) + 1;
double rows = positions.map((o) => o.dy).reduce(max) + 1;
cinemaHallSize = Size(cols * seatSize.width, rows * seatSize.height);
}
#override
void performLayout(ui.Size size) {
final matrix = sizeToRect(cinemaHallSize, Offset.zero & size);
final seatRect = MatrixUtils.transformRect(matrix, Offset.zero & seatSize);
final center = size.center(Offset(-seatRect.width / 2, -seatRect.height / 2));
int childId = 0;
final constraints = BoxConstraints.tight(Size(seatRect.width, seatRect.height));
final t = Curves.bounceOut.transform(ctrl.value);
for (final position in positions) {
layoutChild(childId, constraints);
final offset = Offset(
seatRect.left + position.dx * seatRect.width,
seatRect.top + position.dy * seatRect.height);
positionChild(childId, Offset.lerp(center, offset, t)!);
childId++;
}
}
#override
bool shouldRelayout(covariant MultiChildLayoutDelegate oldDelegate) => false;
}
Matrix4 sizeToRect(Size src, Rect dst, {BoxFit fit = BoxFit.contain, Alignment alignment = Alignment.center}) {
FittedSizes fs = applyBoxFit(fit, src, dst.size);
double scaleX = fs.destination.width / fs.source.width;
double scaleY = fs.destination.height / fs.source.height;
Size fittedSrc = Size(src.width * scaleX, src.height * scaleY);
Rect out = alignment.inscribe(fittedSrc, dst);
return Matrix4.identity()
..translate(out.left, out.top)
..scale(scaleX, scaleY);
}
I tried various manual methods but none worked for me, Then I found This package.
Its Example app has static seats but we can make it dynamic by using List.generate() & State Management

flutter : how to access/manipulate gridview's cell value

Assuming I have
items = [0,0,0,0,0,0,0,0,0]
and I use GridView.builder, to display it with 3 x 3 configuration.
with a trigger of onTap/onPress of button, I want to change a selected cell in that GridView.
for example sake, just +1 on the value 0.
how to achieve this in flutter?
for those who seeks this information
you can use provider and consumer to achieve this.
create a provider class
class ItemProvider with ChangeNotifier {
List<int> _items = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
List<int> get items {
return _items;
}
increase(index) {
_items[index] = _items[index] + 1;
notifyListeners();
}
then use provider-consumer (i used consumer), make sure you also include it in the provider in common dominator (main.dart)
Consumer<ItemProvider>(builder: (context, itemprovider, widget) {
return DragSelectGridView(
physics: NeverScrollableScrollPhysics(),
gridController: theGridcontroller,
itemCount: itemprovider.items.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
crossAxisSpacing: 6,
mainAxisSpacing: 6,
childAspectRatio: 1,
),
itemBuilder: (context, index, isSelected) => SelectableItemWidget(
item: itemprovider.mapping[index],
isSelected: isSelected,
index: index,
),
);
})
and finally, in SelectableItemWidget's class, onTap, u can refer to your methods in ItemProvider class like so
class SelectableItemWidget extends StatefulWidget {
..
....
...
class _SelectableItemWidgetState extends State<SelectableItemWidget> {
...
....
Widget build(BuildContext context) {
return Consumer<ItemProvider>(builder: (context, itemprovider, child) {
....
onTap: () => itemprovider.increase(widget.index),

Flutter scroll screen when pointer reaches edge

I have a GridView that contains draggable items. When an item is dragged to the top/bottom of the screen I want to scroll the GridView in that direction.
Currently I wrapped each draggable item in a Listener like so:
Listener(
child: _wrap(widget.children[i], i),
onPointerMove: (PointerMoveEvent event) {
if (event.position.dy >= MediaQuery.of(context).size.height - 100) {
// 120 is height of your draggable.
widget.scrollController.animateTo(
widget.scrollController.offset + 120,
curve: Curves.easeOut,
duration: const Duration(milliseconds: 200));
}if (event.position.dy <= kToolbarHeight + MediaQueryData.fromWindow(window).padding.top + 100) {
// 120 is height of your draggable.
widget.scrollController.animateTo(
widget.scrollController.offset - 120,
curve: Curves.easeOut,
duration: const Duration(milliseconds: 200));
}
}
)
It works, but the scroll is not smooth at all and looks kind of laggy.
I would need it to work on web too.
Does anyone have a better solution for this?
Here's how I'm solving it. Using TickerProviderStateMixin, you can obtain a Ticker that invokes a callback once per frame, where you can adjust the scroll offset by a small amount for a smooth scroll. I used a Stack to add dummy DragTargets to the top and bottom of the list area which control the tickers. I used two per edge, to allow different scrolling speeds. You could probably use a Listener to interpolate the speed using the cursor position if you want finer-grained control.
https://www.dartpad.dev/acb83fdbbbbb0fd765cd5afa414a8942
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Stack(
children: [
ListView.separated(
controller: controller,
itemCount: 50,
itemBuilder: (context, index) {
return buildLongPressDraggable(index);
},
separatorBuilder: (context, index) {
return Divider();
},
),
Positioned(
top: 0, left: 0, right: 0, height: 25, child: buildEdgeScroller(-10)),
Positioned(
top: 25, left: 0, right: 0, height: 25, child: buildEdgeScroller(-5)),
Positioned(
bottom: 25, left: 0, right: 0, height: 25, child: buildEdgeScroller(5)),
Positioned(
bottom: 0, left: 0, right: 0, height: 25, child: buildEdgeScroller(10)),
],
),
);
}
Widget buildEdgeScroller(double offsetPerFrame) {
return DragTarget<int>(
builder: (context, candidateData, rejectedData) => Container(),
onWillAccept: (data) {
scrollTicker = this.createTicker((elapsed) {
if (!controller.hasClients) {
return;
}
final position = controller.position;
if ((offsetPerFrame < 0 && position.pixels <= position.minScrollExtent) ||
(offsetPerFrame > 0 && position.pixels >= position.maxScrollExtent)) {
scrollTicker.stop();
scrollTicker.dispose();
scrollTicker = null;
} else {
controller.jumpTo(controller.offset + offsetPerFrame);
}
});
scrollTicker.start();
return false;
},
onLeave: (data) {
scrollTicker?.stop();
scrollTicker?.dispose();
scrollTicker = null;
},
);
}

How to change FlutterLogo color?

Earlier there was a colors property which used to change the default color for the Flutter logo. But seems like it is removed. So, how can I change the Flutter logo color?
FlutterLogo(
colors: Colors.red, // Error (now)
)
The FlutterLogo widget doesn't have any properties to change its color but you have kind of a workaround by using the widget ColorFiltered.
Code
class StylizedFlutterLogo extends StatelessWidget {
final ColorFilter colorFilter;
final double? size;
const StylizedFlutterLogo({required this.colorFilter, this.size});
#override
Widget build(BuildContext context) {
return ColorFiltered(
colorFilter: colorFilter,
child: FlutterLogo(size: size),
);
}
}
Sample 1
StylizedFlutterLogo(
colorFilter: ColorFilter.matrix(<double>[
0.2126,
0.7152,
0.0722,
0,
0,
0.2126,
0.7152,
0.0722,
0,
0,
0.2126,
0.7152,
0.0722,
0,
0,
0,
0,
0,
1,
0,
]),
)
Sample 2
StylizedFlutterLogo(
colorFilter: ColorFilter.matrix(<double>[
0.393,
0.769,
0.189,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
1,
0,
]),
)