I am trying to move slider for zoom in flutter camera. I tried many things but I could not get solution. I am attaching my code with screenshot. Please give right suggestion.
double _minAvailableZoom = 2.0;
double _maxAvailableZoom = 2.0;
double _currentZoomLevel = 2.0;
Expanded(
child: Slider(
value: _currentZoomLevel,
max: _maxAvailableZoom,
min: _minAvailableZoom,
activeColor: Colors.white,
inactiveColor: Colors.white30,
onChanged: (value) async {
setState(() {
_currentZoomLevel = value;
});
await cameraController!.setZoomLevel(value);
},
),
),
just do this,
double _minAvailableZoom = 0.0;
double _currentZoomLevel = 0.0;
You have set that maximum available zoom is equal to minimun available zoom so you are unable to zoom. don't add maximum zoom property if it is required the set to some higherpoints as you require to zoom.
Related
I have built a custom slider and have been using GestureDetector with onHorizontalDragUpdate to report drag changes, update the UI and value.
However, when a user lifts their finger, there can sometimes be a small, unintentional hop/drag, enough to adjust the value on the slider and reduce accuracy. How can I stop this occuring?
I have considered adding a small delay to prevent updates if the drag hasn't moved for a tiny period and assessing the primaryDelta, but unsure if this would be fit for purpose or of there is a more routine common practive to prevent this.
--
Example of existing drag logic I am using. The initial drag data is from onHorizontalDragUpdate in _buildThumb. When the slider is rebuilt, the track size and thumb position is calculated in the LayoutBuilder and then the value is calculated based on the thumb position.
double valueForPosition({required double min, required double max}) {
double posIncrements = ((max) / (_divisions));
double posIncrement = (_thumbPosX / (posIncrements));
double incrementVal =
(increment) * (posIncrement + widget.minimumValue).round() +
(widget.minimumValue - widget.minimumValue.truncate());
return incrementVal.clamp(widget.minimumValue, widget.maximumValue);
}
double thumbPositionForValue({required double min, required double max}) {
return (max / (widget.maximumValue - widget.minimumValue - 1)) *
(value - widget.minimumValue - 1);
}
double trackWidthForValue({
required double min,
required double max,
required double thumbPosition,
}) {
return (thumbPosition + (_thumbTouchZoneWidth / 2))
.clamp(min, max)
.toDouble();
}
bool isDragging = false;
bool isSnapping = false;
Widget _buildSlider() {
return SizedBox(
height: _contentHeight,
child: LayoutBuilder(
builder: (context, constraints) {
double minThumbPosX = -(_thumbTouchZoneWidth - _thumbWidth) / 2;
double maxThumbPosX =
constraints.maxWidth - (_thumbTouchZoneWidth / 2);
if (isDragging) {
_thumbPosX = _thumbPosX.clamp(minThumbPosX, maxThumbPosX);
value = valueForPosition(min: minThumbPosX, max: maxThumbPosX);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
widget.onChanged(value);
});
} else {
_thumbPosX = thumbPositionForValue(
min: minThumbPosX,
max: maxThumbPosX,
);
}
double minTrackWidth = 0;
double maxTrackWidth = constraints.maxWidth;
double trackWidth = 0;
if (isDragging) {
trackWidth = (_thumbPosX + (_thumbTouchZoneWidth / 2))
.clamp(_thumbWidth, constraints.maxWidth);
} else {
trackWidth = trackWidthForValue(
min: minTrackWidth,
max: maxTrackWidth,
thumbPosition: _thumbPosX,
);
}
return Stack(
alignment: Alignment.centerLeft,
clipBehavior: Clip.none,
children: [
_buildLabels(),
_buildInactiveTrack(),
Positioned(
width: trackWidth,
child: _buildActiveTrack(),
),
Positioned(
left: _thumbPosX,
child: _buildThumb(),
),
],
);
},
),
);
}
Widget _buildThumb() {
return GestureDetector(
behavior: HitTestBehavior.opaque,
dragStartBehavior: DragStartBehavior.down,
onHorizontalDragUpdate: (details) {
setState(() {
_thumbPosX += details.delta.dx;
isDragging = true;
});
},
child: // Thumb UI
);
}
Updated: I make a little adjustment by adding a delay state and lastChangedTime.
If the user stops dragging for a short period (3 sec), the slider will be locked until the next new value is updated + a short delay (1.5 sec)
I follow your train of thought and make a simple example from Slider widget.
Is the result act like your expected? (You can adjust the Duration to any number)
DartPad: https://dartpad.dev/?id=95f2bd6d004604b3c37f27dd2852cb31
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
double _currentSliderValue = 20;
DateTime lastChangedTime = DateTime.now();
bool isDalying = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(_currentSliderValue.toString()),
const SizedBox(height: 30),
Slider(
value: _currentSliderValue,
max: 100,
label: _currentSliderValue.round().toString(),
onChanged: (double value) async {
if (isDalying) {
await Future.delayed(
Duration(milliseconds: 1500),
() => isDalying = false,
);
} else {
if (DateTime.now().difference(lastChangedTime) >
Duration(seconds: 3)) {
isDalying = true;
} else {
setState(() {
_currentSliderValue = value;
});
}
}
lastChangedTime = DateTime.now();
},
),
],
);
}
}
Hello i'am using Animarker package (https://pub.dev/packages/flutter_animarker) and I cannot clear map markers using it, it is a big problem .
I end up with marker adding each time, carousel list is scrolled, by the user instead of only showing marker one by one.
I have to set the controller like this :
late final Completer<GoogleMapController> _mapController = Completer();
in order to use futur in mapId required Animaker element
Animarker(
mapId: _mapController.future.then<int>((value) => value.mapId),
rippleRadius: 0.6, //[0,1.0] range, how big is the circle
rippleColor: Colors.grey, // Color of fade ripple circle
rippleDuration: Duration(milliseconds: 2500),
markers:_storeMarkers.toSet(),
runExpressAfter: 1,
I have tried solution from this but without success Remove marker in google_maps_flutter
My call is that when this function is called it is clearing the map and then add the new marker
void _carouselCallback(Product product) {
var fe= product.store?.id;
// _mapController.clearMarkers();
//_storeMarkers.remove(_storeMarkers.firstWhere((Marker marker) => marker.markerId.value == '3'));
_storeMarkers.clear();
final marker= RippleMarker(
markerId: MarkerId('${product.store?.id}'),
alpha: 1,
icon: _storePin ?? BitmapDescriptor.defaultMarker,
position: LatLng(product.store?.lat ?? 0, product.store?.long ?? 0),
ripple: false,
onTap: () {
if (product.store != null) {
_carouselModePinCallback(product.store);
}
},
);
_storeMarkers.add(marker);
_moveToStore(product.store);
setState(() {});
}
_storeMarker is a Set
Could someone help ? Thank you
Try to use this:
markers[myMarkerId] = Marker(markerId: myMarkerId, visible: false);
and the problem will be solved.
This is how a Slider's animation normally looks like:
This is how a Slider looks when I try to add the value label:
This is the sample code:
Slider(
value: sliderValue,
activeColor: color,
min: 0.0,
max: 100.0,
divisions: 2000, //TO COMMENT
label: sliderValue.toStringAsFixed(2), //TO COMMENT
onChanged: (value) {
setState(() {
sliderValue = value;
});
}),
In this code, if I comment out the marked //TO COMMENT lines which are the divisions and label properties, the `label goes away as expected, and the animation is smooth again.
I assume this is due to divisions, and any amount of it, even just 100 does not change the lag in any way.
Additionally, it seems that the label property does not work on its own.
It needs the divisions property to also be set so that the value
label can be displayed.
What is the workaround so that I can achieve a Slider with the smoothness shown in the first image, but have the default value label or what looks the same?
If you take a look on source code, you can find _positionAnimationDuration which is responsible to animate the slider
Change it to
static const Duration _positionAnimationDuration = Duration.zero;
Changing on source-code will affect on others project, instead create a local dart file, paste the full slider code and make changes.
Let say we have created customSlider.dart file
. Make sure to replace some(./xyz.dart) top imports with import 'package:flutter/cupertino.dart'; or material on our customSlider.dart.
Then replace _positionAnimationDuration.
To use this, import the file
import 'customSlider.dart' as my_slider;
...
//use case
my_slider.Slider(....)
// create class
// .yaml > another_xlider: ^1.0.0
import 'package:another_xlider/another_xlider.dart';
import '../res/res_controller.dart';
import '../utils/utils_controller.dart';
import 'package:flutter/material.dart';
class RangeBar extends StatelessWidget {
final List<double>? values;
final double? min;
final double? max;
final Function(int, dynamic, dynamic)? onDragging;
const RangeBar(
{Key? key,
#required this.values,
#required this.onDragging,
#required this.min,
#required this.max})
: super(key: key);
#override
Widget build(BuildContext context) {
return FlutterSlider(
values: values!,
// pre set values
rangeSlider: true,
handlerAnimation: FlutterSliderHandlerAnimation(
curve: Curves.elasticOut,
reverseCurve: Curves.bounceIn,
duration: Duration(milliseconds: 500),
scale: 1.5),
jump: true,
min: min ?? 0,
max: max ?? 0,
touchSize: Sizes.s13,
trackBar: FlutterSliderTrackBar(
activeTrackBar: BoxDecoration(color: AppColors.orange),
),
tooltip: FlutterSliderTooltip(
boxStyle: FlutterSliderTooltipBox(
decoration: BoxDecoration(
color: Colors.greenAccent,
borderRadius: BorderRadius.all(Radius.circular(Sizes.s5)),
border: Border.all(
color: AppColors.steelGray,
),
),
),
positionOffset: FlutterSliderTooltipPositionOffset(top: -Sizes.s15),
alwaysShowTooltip: true,
textStyle:
TextStyles.defaultRegular.copyWith(fontSize: FontSizes.s11),
),
onDragging: onDragging);
}
}
// try to call
Container(
child: _size(),
)
Widget _size() {
{
double sizeMin;
double sizeMax;
sizeMin = 0;
sizeMax = 0;
sizeMax = sizeMin.round() == sizeMax.round() ? sizeMax + 1 : sizeMax;
return RangeBar(
values: [
// add values
],
min: sizeMin,
max: sizeMax.ceilToDouble(),
onDragging: (p0, lowerValue, upperValue) {
log(lowerValue);
log(upperValue);
},
);
return C0();
}
}
I am creating a note-taking app using Flutter and I use a CustomPainter for the drawing part. For better performance, the CustomPaint gets converted into an image after some Lines. Now the problem with images is when zooming in, the lines (which are Images now) are very pixelated. Now my first Idea to solve this was redrawing the lines after zooming.
But how do I redraw them correctly without them still having a bad resolution? Also, is it possible to only redraw the lines that are visible on the screen currently?
everything is wrapped with a layoutbuilder which provides constraints + pixelratio (scale):
LayoutBuilder(builder: (context, constraints) {
size = constraints.biggest;
scale = MediaQuery.of(context).devicePixelRatio;
How I did the zooming:
GestureDetector(
onScaleStart: (details) {
_initialFocalPoint = details.focalPoint;
_initialScale = _scale;
},
onScaleUpdate: (details) {
setState(() {
_sessionOffset =
details.focalPoint - _initialFocalPoint;
_scale = _initialScale * details.scale;
});
},
onScaleEnd: (details) {
setState(() {
_offset += _sessionOffset;
_sessionOffset = Offset.zero;
});
},
child: Transform.translate(
offset: _offset + _sessionOffset,
child: Transform.scale(
scale: _scale,
child: ....
my CustomPaint which comes somewhere after the Transform widgets:
CustomPaint(
size: size!,
willChange: true,
painter: DrawingViewPainter(
pointsList: drawingNotifier.points,
image: image
),
);
snippet with the custpmpaint to image converter (drawn lines get converted to images to save performance):
ui.PictureRecorder recorder = ui.PictureRecorder();
Canvas canvas = ui.Canvas(recorder);
canvas.scale(scale!);
DrawingViewPainter(image: image, pointsList: points).paint(canvas, size!);
ui.Picture picture = recorder.endRecording();
ui.Image newImage = await picture.toImage(
(size!.width * scale!).ceil(),
(size!.height * scale!).ceil(),
);
......
image = newImage
the Custompaint part with the image drawer:
canvas.drawImageRect(
image!,
Offset.zero & Size(image!.width.toDouble(), image!.height.toDouble()),
Offset.zero & size,
Paint());
}
(sorry for the huge amount of code)
If you know of a better way please let me know as well.
Here a video of the Problem
My slider jumps from one value to another. How can I make it movable in between the values or make it smoother in Flutter?
Column(
children: <Widget>[
RangeSlider(
min: 1,
max: 40,
divisions: 4,
onChanged: onChange,
values: RangeValues((startValue ?? 1).toDouble(), (endValue ?? 40).toDouble()),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Text('Beginner', style: Theme.of(context).textTheme.body2),
Text('Ameateur', style: Theme.of(context).textTheme.body2),
Text('Intermediate', style: Theme.of(context).textTheme.body2),
Text('Advanced', style: Theme.of(context).textTheme.body2),
Text('Professional', style: Theme.of(context).textTheme.body2),
],
),
],
);
The divisions property makes it jump in 4 steps. Remove that and the slider will be fluent.
RangeSlider(
min: 1,
max: 40,
// divisions: 4, // <-- remove this
onChanged: onChange,
values: RangeValues((startValue ?? 1).toDouble(), (endValue ?? 40).toDouble()),
),
Update after question clarification
Question is actually about having the thumbs being fluent while still snapping to discrete points on the range slider. The Flutter widget does not provide an easy way to achieve that, so there are two options.
Option 1: custom widget
The first is to create a new widget to get the exact behavior you need. This might be the best option if you need a lot of customisation.
Option 2: hack the RangeSlider widget
Use the onChangeEnd callback in addition to the onChanged callback to update the position of the thumb correctly. The onChanged callback is used to make it fluent, while the onChangeEnd callback is used to snap the thumb to the closest discrete value based on a division count.
A possible implementation (the discretize method can possibly be shorter/improved):
class _RangeSliderExampleState extends State<RangeSliderExample> {
double _startValue;
double _endValue;
static const double _minValue = 1;
static const double _maxValue = 40;
static const double _divisions = 4;
#override
Widget build(BuildContext context) {
return RangeSlider(
min: _minValue,
max: _maxValue,
onChanged: (values) {
setState(() {
_startValue = values.start;
_endValue = values.end;
});
},
onChangeEnd: (values) {
setState(() {
_startValue = _discretize(values.start, _divisions);
_endValue = _discretize(values.end, _divisions);
});
},
values: RangeValues((_startValue ?? 1).toDouble(),
(_endValue ?? 40).toDouble()),
);
}
double _discretize(double value, double divisions) {
double x = value - _minValue;
double range = _maxValue - _minValue;
double result = x / range;
return (result * divisions).round() / divisions * range + _minValue;
}
}