fluttercandies extended_image. How do I zoom in on an image with double tap of 1 finger instead of needing 2? - image

https://github.com/fluttercandies/extended_image
https://github.com/fluttercandies/extended_image/tree/master/example
https://github.com/fluttercandies/extended_image/blob/master/example/lib/pages/zoom_image_demo.dart
fluttercandies extended_image. How do I zoom in on an image with double tap of 1 finger instead of needing 2? How do I get a sliding image to navigator pop back when it slides off the page?
Trying to implement a zoom/pan image with double tap by 1 finger instead of the need for 2 to expand zoom an image. Have two issues with this code and was wondering if anyone has any ideas. The class is very simple with just 2 strings: image & title passed into it.
1, I need the image to expand on double tap. I would like the user to have to power to expand the image with one finger and not two. Think I need to put this code near the very end.
The good thing is that once it is expanded the double tap works to reduce the image size. How do I get it to do the opposite when it is at normal size?
2, the sliding of the image off the page results in a black screen. Thankfully, this doesn’t freeze or crash the app but it leaves the user with a blank screen and the need to press the system back button. I would like the slide to result in a navigator pop back to the original screen.
Firstly, here’s a sample code of how I’m passing an image and a title into expandimage.dart.
FlatButton(
            child: Image.asset(_kAsset5),
            onPressed: () async {
              Navigator.push(
                context,
                MaterialPageRoute(
                    builder: (context) => ExpandImage(
                          image: _kAsset5,
                          title: "\'go help\' 1",
                        )),
              );
            },
          ),
Here’s the code that I’m using for this ‘expandimage.dart’ and a lot of it is based on the pan/zoom example from flutter candies / extended image example.
import 'package:flutter/material.dart';
import 'package:extended_image/extended_image.dart';
class ExpandImage extends StatelessWidget {
  final String image, title;
  ExpandImage({this.image, this.title});
  #override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.red[900],
        appBar: AppBar(
          backgroundColor: Colors.red[900],
          leading: IconButton(
            icon: Icon(Icons.close),
            onPressed: Navigator.of(context).pop,
          ),
          title: Text(
            title,
            style: TextStyle(
              color: Colors.yellow,
              inherit: true,
              fontWeight: FontWeight.w300,
              fontStyle: FontStyle.italic,
              letterSpacing: 2.0, //1.2
            ),
          ),
          centerTitle: true,
        ),
        body: SizedBox.expand(
          // child: Hero(
          // tag: heroTag,
          child: ExtendedImageSlidePage(
            slideAxis: SlideAxis.both,
            slideType: SlideType.onlyImage,
            child: ExtendedImage(
              //disable to stop image sliding off page && entering dead end without back button.
//setting to false means it won't slide at all.
              enableSlideOutPage: true,
              mode: ExtendedImageMode.gesture,
              initGestureConfigHandler: (state) => GestureConfig(
                minScale: 1.0,
                animationMinScale: 0.8,
                maxScale: 3.0,
                animationMaxScale: 3.5,
                speed: 1.0,
                inertialSpeed: 100.0,
                initialScale: 1.0,
                inPageView: false,
              ),
              // onDoubleTap: ? zoom in on image
              fit: BoxFit.scaleDown,
              image: AssetImage(
                image,
              ),
            ),
          ),
        ),
      ),
    );
  }
}
Here is a sample image passed in. the page turns red when sliding the image around and then it goes black as the image slides off the page.

Hope it will help, if have any doubt ask in the comments.
class _DetailState extends State<Detail> with TickerProviderStateMixin{
#override
Widget build(BuildContext context) {
AnimationController _animationController = AnimationController(duration: Duration(milliseconds: 200),vsync: this);
Function() animationListener = () {};
Animation? _animation;
return Scaffold(
body: Container(
height: MediaQuery.of(context).size.height,
child: ExtendedImage.network(
widget.wallpaper.path,
fit: BoxFit.contain,
mode: ExtendedImageMode.gesture,
initGestureConfigHandler: (state) {
return GestureConfig(
minScale: 0.9,
animationMinScale: 0.7,
maxScale: 3.0,
animationMaxScale: 3.5,
speed: 1.0,
inertialSpeed: 100.0,
initialScale: 1.0,
inPageView: false,
initialAlignment: InitialAlignment.center,
);
},
onDoubleTap: (ExtendedImageGestureState state) {
///you can use define pointerDownPosition as you can,
///default value is double tap pointer down postion.
var pointerDownPosition = state.pointerDownPosition;
double begin = state.gestureDetails!.totalScale!;
double end;
_animation?.removeListener(animationListener);
_animationController.stop();
_animationController.reset();
if (begin == 1) {
end = 1.5;
} else {
end = 1;
}
animationListener = () {
//print(_animation.value);
state.handleDoubleTap(
scale: _animation!.value,
doubleTapPosition: pointerDownPosition);
};
_animation = _animationController
.drive(Tween<double>(begin: begin, end: end));
_animation!.addListener(animationListener);
_animationController.forward();
},
),
),
}
}
If it helps, mark it as right.

Related

How to create a picture area in Flutter?

In their example https://flutter.dev/docs/cookbook/plugins/picture-using-camera we can take pictures using the camera plugin.
I modified the code and move into a separate widget. My main goal is to try to implement a picture area (like QRCode style) and take the picture and if necessary tell the user to corp the image or my be app will do it automatically. There is a barcode_scan plugin. It shows an area to scan the barcode. I like to implement that part to take pictures of an item.
https://codecanyon.net/item/qr-code-barcode-scanner-and-generator-for-ios-swift-with-admob/screenshots/20280521?index=1 screenshot2 has a 4 half square bracket on 4 edges. I like to make it similar but capture images only.
I am new to this plugin and corp idea. How can I create a picture area in Flutter so the user can center the item in that area and take a picture.?
Widget _buildPictureArea(BuildContext context) {
return new Container(
width: 200,
height: 200,
child: FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the Future is complete, display the preview
return CameraPreview(_controller);
} else {
// Otherwise, display a loading indicator
return Center(child: CircularProgressIndicator());
}
},
),
);
}

Create very custom animations in flutter

I've followed the various animation tutorials on flutter.io, (tween, stagger, transitions, etc.) and its all great.
What I would like to explore is how to actually make custom animations based on the composition of a UI object.
Lets take a simple example, a Pause -> Play animation.
At first we have a Pause icon, two vertical bars.
Let's say I would like to
Grow the right bar into a triangle by adding an extra corner on the center of the rightmost vertical side, and moving it to the right.
After that moving that triangle from step 1 slightly to the left, so it now sticks to the leftmost vertical bar, into a bigger "triangle" (that'd be a pentagon actually).
That would look like a play button, and not a pause button anymore.
How would I achieve that kind of custom animation ? I'm assuming I can't work with the icons class. And I'm pretty sure I shouldn't do that with Widgets and just move them around.
Where would I go to start exploring that kind of precision in animations?
The answer from #Alaric points you at a couple of packages but doesn't really give any justification for why you'd use them.
The issue at hand is that the animation you're talking about is moderately complicated in terms of how it actually works. There are multiple items which change over time and possibly even become one bigger item.
There are two approaches you could take to solving this problem. The first is to use an external animation tool to create this animation, using whichever features the animation tool has to do item changing and merging. Then once you have an animation which runs to your satisfaction, you have to import it into your project somehow. That's where the fluttie and flare_flutter plugins come in - if you used Aftereffects, you use Lottie to export the file and then the fluttie plugin to show it. Flare is slightly simpler as it's meant for flutter, but you still create the animation externally and then add the file to your assets to be rendered.
The other approach is to do the animation yourself. That entails three things:
Creating a widget to contain the animation.
Creating a CustomPainter to actually draw the result.
Optionally, another class which acts as controller to start/stop/etc the animation.
The widget containing the animation could probably also be the controller if you use a GlobalKey to access it and expose start/stop methods, but that's a bit messy. It's better to have an external object that is the controller - and you could probably even use an AnimationController as-is although it would be less 'clean'.
If you don't pass it in, you'd probably have an AnimationController in your widget that you start and stop from your controller or class. It would essentially just go from 0 to 1 and back, and would be responsible for rebuilding the CustomPainter (probably using an AnimatedBuilder).
This is a very basic example that doesn't need an external controller as the gesture detection happens within the widget. Note that I'm not calling setState every time the 'started' member is set, because I don't actually want it to rebuild when it changes.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: StartStop(),
),
),
);
}
}
class StartStop extends StatefulWidget {
#override
StartStopState createState() {
return new StartStopState();
}
}
class StartStopState extends State<StartStop> with TickerProviderStateMixin<StartStop> {
bool started = false;
AnimationController animationController;
#override
void initState() {
animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 300));
super.initState();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
started ? animationController.forward() : animationController.reverse();
started = !started;
},
child: SizedBox(
width: 100,
height: 100,
child: AnimatedBuilder(
animation: animationController,
builder: (context, child) {
return CustomPaint(
painter: StartStopPainter(animationController.value),
size: Size.infinite,
child: child,
);
},
),
),
);
}
}
class StartStopPainter extends CustomPainter {
final double percentAnimated;
StartStopPainter(this.percentAnimated) : assert(percentAnimated >= 0 && percentAnimated <= 1);
#override
void paint(Canvas canvas, Size size) {
var pausePaint = Paint()..color = Colors.black.withOpacity(1 - percentAnimated);
canvas.drawRect(Rect.fromLTRB(20, 10, 40, 90), pausePaint);
canvas.drawRect(Rect.fromLTRB(60, 10, 80, 90), pausePaint);
var playPaint = Paint()..color = Colors.black.withOpacity(percentAnimated);
canvas.drawPath(Path()..addPolygon([Offset(20, 10), Offset(20, 90), Offset(80, 50)], true), playPaint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
I'll leave the actual custom part of the animation (where you make the rectangle change to a triangle etc) to you. Instead of using opacity and a few different paint calls, you'd simply be using the input percentAnimated to decide which path or polygon to draw.

Performant blur/opacity in Flutter?

I love the blurry frost effect using a BackdropFilter (see this).
However, because the BackdropFilter has Opacity and because the widget I'm blurring also has Opacity, the performance is horrendous. This is also because I redraw my widgets a few times a second, but that shouldn't be an issue given Flutter can go 60fps?
I turned on checkerboardOffscreenLayers and see checkerboards for miles. :O
The checkerboards happen due to blurScreen, not due to widgetToBlur but widgetToBlur does slow down performance probably because (in my real code, not this example) it's calling setState() multiple times a second.
Is there a more performant way to make blurs/opacities? The link above says to apply opacity to widgets individually. I can't do that with the blur though (blurScreen below), because the BackdropFilter has to be stacked on top of my widget-that-does-the-redrawing.
I removed the blur effect and my performance is way better (no checkerboards, app doesn't crash).
build() code in question:
final widgetToBlur = Container(
child: Opacity(
opacity: 0.3,
// In my actual code, this is a Stateful widget.
child: Text('Spooky blurry semi-transparent text!'),
),
);
final blurScreen = BackdropFilter(
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
child: Container(
decoration: BoxDecoration(
color: _backgroundColor.withOpacity(0.3),
),
),
);
return Container(
child: Stack(
children: <Widget>[
widgetToBlur,
blurScreen,
Text('This is in front of the blurred background'),
],
),
);
I ended up drawing the widgetToBlur once, blurred, with opacity, using Paint and Canvas.
This means it only runs the blur and opacity operations once, in initState(), and never has to be re-rendered-with-blur throughout the lifecycle of the widget.
If anyone else ends up stuck with this, you can leave a comment and I can help out more.
I have a similar problem and it also boils down to using canvas and paint.
The only problem now is, if I apply a MaskFilter to the image, not much happens despite the enormously high sigma... Only the edge is a little blurred.
Now the question is why is that so? How did you solved this issue?
canvas.drawImageRect(
image,
Offset(0, 0) & srcSize,
Offset(-delta, 0) & dstSize,
Paint()..maskFilter = MaskFilter.blur(
BlurStyle.normal, 100.0
)
);
For those of you who are interested, I have loaded the image in the init function as follows:
rootBundle.load("assets/gift_1.png").then((bd) {
Uint8List lst = new Uint8List.view(bd.buffer);
Ui.instantiateImageCodec(lst).then((codec) {
codec.getNextFrame().then((frameInfo) {
image = frameInfo.image;
});
});
});
P.s. Unfortunately, I can't write any comments yet; therefore here as a contribution, including solution proposal
you can use this. but I do not know speed.
var pr = ui.PictureRecorder();
var canvas = ui.Canvas(pr);
canvas.drawImage(
img,
ui.Offset(0, 0),
ui.Paint()..maskFilter = ui.MaskFilter.blur(
BlurStyle.normal, 100.0
)
);
var pic = pr.endRecording();
return pic.toImage(img.width, img.height);

In React Native - How to move View element in parallely while ScrollView scrolls

I'm developing a TimeLine component. There are Views list inside horizontal ScrollView that represents half an hour blocks. And I have a Component called TimeRangeSelector, which I use to select a range of time in TimeLine ScrollView. So while I scroll the ScrollView I need to move the TimeRangeSelector in parallel without any lag. Below is the TimeLine component. You can see the 30mins blocks are filled inside ScrollView. The yellow color one is the TimeRangeSelector which is a Animated.View. And the left position of the TimeRangeSelector is set using the scroll position. Each time ScrollView moves im setting the state as below.
<ScrollView
horizontal={true}
onScroll={this.onScrollFunc}
>
{timelineElements}
</ScrollView>
onScrollFunc = (event, gesture) => {
this.setState({
contentOffsetX: event.nativeEvent.contentOffset.x,
scrollStopped: false
});
};
And Im passing the scrollBarPosition as props to the TimeRangeSelector and setting its left position style as shown in below code
<TimeRangeSelector
scrollBarPosition={this.state.contentOffsetX}
/>
let styles= [
{
position: "absolute",
left: this.props.scrollBarPosition,
backgroundColor: backgroundColor,
marginTop: 20,
marginLeft: 1,
width: this.state.selectionWidth,
height: 100
}
];
But the issue is when I scroll the ScrollView the TimeRangeSelector moves with it, but it has a delay.. when I scroll faster the distance between where it should be and where it is, is becoming high. Can anyone give me some ideas according to your knowledge.
My assumption: According to my understanding, I think the lag is because of it takes several instances to reach the setState in and set the ScrollBarPosition as below steps.
ScrollView moved 1 frame.
ScrollView fires the onScroll function and emmits the event with new x point.
Then in the onScroll function it sets the State.
As I understand it takes some time to happen all these steps from JavaScript thread all the way to Native Thread and come back to JS thread again.
You should use something like const onScroll = Animated.event([{ nativeEvent: { contentOffset: { x: animatedValue } } }], { useNativeDriver: true }); with const animatedValue = new Animated.Value(0). This way the animation is only done on the native level without going back and forth through the JS thread.
This animated value can only be used effectively with opacity and transform style properties, but it should help you with your problem.
You can read more about this, in this awesome article.

React Native: Creating an Animated View that slides directly with finger drag

Desired Effect:
I want to create a basic animation of a smooth sliding View (left or right) in sync with the drag pace of my finger.(e.g. sliding an off- canvas menu into and out of view)
Currently I'm using Animated.spring in a parent component that handles all gestures. Then I'm using transform,translateX in a child component to slide a View left or right.
For Example:
Root.js(Parent Component that handles gestures)
_handlePanResponderMove(e: Object, gestureState: Object) {
let dx = gestureState.moveX - this.state.swipeStart;
Animated.spring(
this.state.slideValue,
{
toValue: newValue,
velocity: gestureState.vx,
tension: 2,
friction: 8,
}).start();
}
Navigation.js(Child Component that slides)
render(){
<Animated.View style={[styles.navigation,{transform: [{translateX: this.props.slideValue}]}]}>
<View >
</View>
</Animated.View>
}
The Problem:
There is sticky/lagging behavior with animation instead of a smooth movement that paces the finger sliding guesture.
My reasoning so far:
From my limited Animation experience - Animated.spring, Animated.ease and Animated.timing don't really describe well the equally paced sliding movement I'm after - but I suppose I need to be using one of them to get optimized native performance
(otherwise I'd just use .setState(slideValue) and do some math with the current location of my finger to figure the position of the View.)
Question:
What would be the preferred way to describe this type of smooth sliding animation using the optimized React-Native Animated library?
What I've tried out:
1) Using Animated.timing and setting duration low and easing to linear(my best guess at what I should do)
Animated.timing(
this.state.navSlideValue,
{
toValue: newValue,
duration: 10,
easing: Easing.linear
}).start();
2) Moving up the tension on Animated.spring
Animated.spring(
this.state.navSlideValue,
{
toValue: newValue,
velocity: (gestureState.vx),
tension: 300,
friction: 30,
}).start();
The preset functions timing, spring are useful for running animations to a specified value with a given easing. If you want to tie an Animated value to an event, you can use Animated.event instead:
PanResponder.create({
// on each move event, set slideValue to gestureState.dx -- the
// first value `null` ignores the first `event` argument
onPanResponderMove: Animated.event([
null, {dx: this.state.slideValue}
])
// ...rest of your panResponder handlers
});

Resources