使用 AnimatedBuilder 定位动画小部件

问题描述 投票:0回答:1

鉴于这些宽度:

const DESKTOP_RESULT_DETAILS_WIDTH = 370.0;
const DESKTOP_SEARCH_RESULTS_WIDTH = 320.0;
_mapWidth =
    MediaQuery.of(context).size.width - DESKTOP_SEARCH_RESULTS_WIDTH;

当名为

Position(left: DESKTOP_SEARCH_RESULTS_WIDTH)
的变量为空时,我正在尝试将小部件动画化为
_model
,而当
Position(left: DESKTOP_SEARCH_RESULTS_WIDTH + DESKTOP_RESULT_DETAILS_WIDTH)
不为空时为
_model

问题是在每个动画开始时,它从错误的位置开始动画,然后在正确的位置结束。谁能看到我的代码的问题?:

AnimationController 定义:

  @override
  void initState() {
    _mapAc = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 1000),
      value: 0,
    );
    super.initState();
  }

计算左侧位置:

  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);
    }
  }

动画定义和触发动画的代码:

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));
  }
});

动画建造者:

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,
        ),
      ),
    );
  },
)
flutter flutter-animation
1个回答
0
投票

希望有帮助

// 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'),
    );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.