Flutter InteractiveViewer onInteractionEnd возврат к масштабу 1.0

#flutter #dart

#флаттер #dart

Вопрос:

Я бы хотел, чтобы изображение, которое я масштабирую, возвращалось к исходному значению масштаба (1.0), когда оно будет выпущено.

onInteractionEnd кажется подходящим свойством для этого, но я не уверен, как получить доступ к свойству scale для создания функции, которая это делает.

       child: InteractiveViewer(
                boundaryMargin: EdgeInsets.all(0.0),
                minScale: 1.0,
                maxScale: 2.5,
                onInteractionEnd: //scale = 1.0,
  

Ответ №1:

Вы можете скопировать вставить запустить полный код ниже
, это модификация официального примера transformationController https://api.flutter.dev/flutter/widgets/InteractiveViewer/transformationController.html
Всякий раз, когда дочерний элемент преобразуется, Matrix4 значение обновляется, и все listeners получают уведомление. Если значение установлено, InteractiveViewer будет обновляться с учетом нового значения.
Вы можете в onInteractionEnd анимации сброса в качестве официальной демонстрации выполнить
фрагмент кода

 void _animateResetInitialize() {
    _controllerReset.reset();
    _animationReset = Matrix4Tween(
      begin: _transformationController.value,
      end: Matrix4.identity(),
    ).animate(_controllerReset);
    _animationReset.addListener(_onAnimateReset);
    _controllerReset.forward();
  }
  
void _onInteractionEnd(ScaleEndDetails details) {
    _animateResetInitialize();
  }
  

рабочая демонстрация

введите описание изображения здесь

полный код

 import 'package:flutter/material.dart';

void main() => runApp(MyApp());

/// This is the main application widget.
class MyApp extends StatelessWidget {
  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _title,
      home: MyStatefulWidget(),
    );
  }
}

/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
  MyStatefulWidget({Key key}) : super(key: key);

  @override
  _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}

/// This is the private State class that goes with MyStatefulWidget.
/// AnimationControllers can be created with `vsync: this` because of TickerProviderStateMixin.
class _MyStatefulWidgetState extends State<MyStatefulWidget>
    with TickerProviderStateMixin {
  final TransformationController _transformationController =
  TransformationController();
  Animation<Matrix4> _animationReset;
  AnimationController _controllerReset;

  void _onAnimateReset() {
    _transformationController.value = _animationReset.value;
    if (!_controllerReset.isAnimating) {
      _animationReset?.removeListener(_onAnimateReset);
      _animationReset = null;
      _controllerReset.reset();
    }
  }

  void _animateResetInitialize() {
    _controllerReset.reset();
    _animationReset = Matrix4Tween(
      begin: _transformationController.value,
      end: Matrix4.identity(),
    ).animate(_controllerReset);
    _animationReset.addListener(_onAnimateReset);
    _controllerReset.forward();
  }

// Stop a running reset to home transform animation.
  void _animateResetStop() {
    _controllerReset.stop();
    _animationReset?.removeListener(_onAnimateReset);
    _animationReset = null;
    _controllerReset.reset();
  }

  void _onInteractionStart(ScaleStartDetails details) {
    // If the user tries to cause a transformation while the reset animation is
    // running, cancel the reset animation.
    if (_controllerReset.status == AnimationStatus.forward) {
      _animateResetStop();
    }
  }

  void _onInteractionEnd(ScaleEndDetails details) {
    _animateResetInitialize();
  }

  @override
  void initState() {
    super.initState();
    _controllerReset = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 400),
    );
  }

  @override
  void dispose() {
    _controllerReset.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Theme.of(context).colorScheme.primary,
      appBar: AppBar(
        automaticallyImplyLeading: false,
        title: const Text('Controller demo'),
      ),
      body: Center(
        child: InteractiveViewer(
          boundaryMargin: EdgeInsets.all(double.infinity),
          transformationController: _transformationController,
          minScale: 1.0,
          maxScale: 2.5,
          onInteractionStart: _onInteractionStart,
          onInteractionEnd: _onInteractionEnd,
          child: Image.network("https://picsum.photos/250?image=9")
        ),
      ),
      persistentFooterButtons: [
        IconButton(
          onPressed: _animateResetInitialize,
          tooltip: 'Reset',
          color: Theme.of(context).colorScheme.surface,
          icon: const Icon(Icons.replay),
        ),
      ],
    );
  }
}
  

Комментарии:

1. Спасибо! Отлично работает. Для нескольких изображений на одном экране вы бы делали все это повторно или часть этого кода может применяться к нескольким изображениям?

2. @chrisfavritz, не могли бы вы опубликовать свой код воспроизведения для нового вопроса. необходимо воспроизвести макет нескольких изображений. Спасибо.

3. @chunhunghan Просто потрясающе!

Ответ №2:

Я придумал простое решение.

Просто сохраните начальное значение контроллера в начале взаимодействия, а затем вернитесь к этому значению, когда взаимодействие закончится.

 TransformationController controllerT = TransformationController();
var initialControllerValue;

InteractiveViewer(
    minScale: 1.0,
    maxScale: 100.0,
    transformationController: controllerT,
    onInteractionStart: (details){
        initialControllerValue = controllerT.value;
    },
    onInteractionEnd: (details){
        controllerT.value = initialControllerValue;
    },
...
  

Если вы хотите это в виде таблицы или списка, просто объявите контроллер в itemBuilder:

 ListView.builder(
    itemCount: widget.types.length,
    itemBuilder: (context, index) {
        TransformationController controllerT = TransformationController();
        var initialControllerValue;