Given these widths:
const DESKTOP_RESULT_DETAILS_WIDTH = 370.0;
const DESKTOP_SEARCH_RESULTS_WIDTH = 320.0;
_mapWidth =
MediaQuery.of(context).size.width - DESKTOP_SEARCH_RESULTS_WIDTH;
I am trying to animate a widget to Position(left: DESKTOP_SEARCH_RESULTS_WIDTH)
when a variable called _model
is null, and Position(left: DESKTOP_SEARCH_RESULTS_WIDTH + DESKTOP_RESULT_DETAILS_WIDTH)
when _model
is not null.
The problem is that at the start of each animation, it starts the animation from the wrong position and then ends in the correct position. Can anyone see the issue with my code?:
The AnimationController definition:
@override
void initState() {
_mapAc = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1000),
value: 0,
);
super.initState();
}
Calculate the left position:
double _mapLeft({required SearchResultModel? model}) {
if (model == null) {
return (_mapWidth * _mapAc.value) + DESKTOP_SEARCH_RESULTS_WIDTH;
} else {
return _mapAc.value *
(DESKTOP_SEARCH_RESULTS_WIDTH + DESKTOP_RESULT_DETAILS_WIDTH);
}
}
The animation definition and the code that triggers the animation:
final animation = Tween(
begin: DESKTOP_SEARCH_RESULTS_WIDTH,
end: DESKTOP_SEARCH_RESULTS_WIDTH + DESKTOP_RESULT_DETAILS_WIDTH)
.animate(_mapAc);
ref.listen<SearchResultModel?>(vgnItmEstDetailsProvider, (previous, next) {
if (next != null) {
_mapAc.animateTo(1, duration: Duration(milliseconds: 1000));
} else {
_mapAc.animateTo(0, duration: Duration(milliseconds: 1000));
}
});
The animated builder:
AnimatedBuilder(
animation: animation,
builder: (context, child) {
return Positioned(
left: _mapLeft(model: _model),
child: SizedBox(
width: MediaQuery.of(context).size.width -
DESKTOP_SEARCH_RESULTS_WIDTH,
height: MediaQuery.of(context).size.height - TOP_NAV_HEIGHT,
child: MapWidget(
vm,
),
),
);
},
)
hope it helps
// ignore_for_file: constant_identifier_names
import 'package:flutter/material.dart';
void main() {
runApp(
const MaterialApp(
home: MyApp(),
),
);
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
const DESKTOP_RESULT_DETAILS_WIDTH = 370.0;
const DESKTOP_SEARCH_RESULTS_WIDTH = 320.0;
const TOP_NAV_HEIGHT = 64.0;
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
bool hasModel = false;
late AnimationController _mapAc;
@override
void initState() {
_mapAc = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
super.initState();
}
double _mapLeft({required bool hasModel, required double animationValue}) {
final mapWidth =
MediaQuery.of(context).size.width - DESKTOP_SEARCH_RESULTS_WIDTH;
final positionWhereMapShouldBeIfHasModel =
DESKTOP_SEARCH_RESULTS_WIDTH + DESKTOP_RESULT_DETAILS_WIDTH;
final positionWhereMapShouldBeIfHasNoModel = DESKTOP_SEARCH_RESULTS_WIDTH;
final animationDiff = positionWhereMapShouldBeIfHasModel -
positionWhereMapShouldBeIfHasNoModel;
return positionWhereMapShouldBeIfHasNoModel +
(animationDiff * animationValue);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: ElevatedButton(
onPressed: () {
setState(() {
hasModel = !hasModel;
if (hasModel) {
_mapAc.animateTo(1);
} else {
_mapAc.animateTo(0);
}
});
},
child: const Text('Animate'),
),
),
body: Stack(
children: [
AnimatedBuilder(
animation: _mapAc,
builder: (context, child) {
return Positioned(
left:
_mapLeft(hasModel: hasModel, animationValue: _mapAc.value),
child: SizedBox(
width: MediaQuery.of(context).size.width -
DESKTOP_SEARCH_RESULTS_WIDTH,
height: MediaQuery.of(context).size.height - TOP_NAV_HEIGHT,
child: child,
),
);
},
child: const MapWidget(),
),
],
),
);
}
}
class MapWidget extends StatelessWidget {
const MapWidget({super.key});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.red),
),
child: const Text('Map'),
);
}
}