簡體   English   中英

帶有畫廊閃爍的英雄動畫 Flutter

[英]Hero animation with gallery flicker Flutter

我正在嘗試在畫廊網格視圖頁面(使用照片管理器插件)和詳細信息頁面之間創建基本的英雄動畫。 當英雄動畫前后都完成時,畫面閃爍。

這是我制作的示例:

在此處輸入圖像描述

完整的代碼可運行:

import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:photo_manager/photo_manager.dart';

void main() {
  runApp(const TestApp());
}

class TestApp extends StatefulWidget {
  const TestApp({Key? key}) : super(key: key);

  @override
  State<TestApp> createState() => _TestAppState();
}

class _TestAppState extends State<TestApp> {
  final List<AssetEntity> assetsList = [];

  bool granted = false;

  void loadAssets() async {
    granted = await PhotoManager.requestPermission();
    if (granted) {
      FilterOptionGroup option = FilterOptionGroup()
        ..addOrderOption(const OrderOption(
          type: OrderOptionType.createDate,
          asc: false,
        ));

      final albums = await PhotoManager.getAssetPathList(filterOption: option, type: RequestType.image);

      print("albums : $albums");
      if (albums.isNotEmpty) {
        var alb = albums.where((element) {
          return element.name == 'Test';
        });
        var album = alb.first;
        // Now that we got the album, fetch all the assets it contains
        List<AssetEntity> currentList =
            await album.getAssetListRange(start: 0, end: 200);
        // Update the state and notify UI
        assetsList.clear();
        assetsList.addAll(currentList);
      }
      setState(() {});
    }
  }

  @override
  void initState() {
    loadAssets();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          body: granted
              ? _gridView()
              : Center(
                  child: Container(
                      color: Colors.blue,
                      width: 200,
                      height: 200,
                      child: TextButton(
                        onPressed: () async {
                          granted = await PhotoManager.requestPermission();
                          setState(() {});
                        },
                        child: const Text(
                          "Ask permission",
                          style: TextStyle(color: Colors.white),
                        ),
                      )),
                )),
    );
  }

  Widget _gridView() {
    return GridView.builder(
        itemCount: assetsList.length,
        gridDelegate:
            const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
        itemBuilder: (context, index) {
          return Hero(
              createRectTween: (Rect? begin, Rect? end) {
                RectTween _rectTween = RectTween(begin: begin, end: end);
                return _rectTween;
              },
              tag: assetsList[index].id,
              child: GalleryThumbnail(
                asset: assetsList[index],
                onTap: (bytes) {
                  Navigator.push(context, MaterialPageRoute(builder: (context) {
                    return DetailsPage(asset: assetsList[index], bytes: bytes);
                  }));
                },
              ));
        });
  }
}

class GalleryThumbnail extends StatelessWidget {
  final AssetEntity asset;
  final ValueChanged<Uint8List> onTap;

  const GalleryThumbnail({Key? key, required this.asset, required this.onTap})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<Uint8List?>(
      future: Platform.isIOS
          ? asset.thumbDataWithOption(
              ThumbOption.ios(
                  width: 500,
                  height: 500,
                  deliveryMode: DeliveryMode.opportunistic,
                  resizeMode: ResizeMode.fast,
                  resizeContentMode: ResizeContentMode.fit,
                  quality: 100
                  // resizeContentMode: ResizeContentMode.fill,
                  ),
            )
          : asset.thumbDataWithSize(250, 250),
      builder: (_, snapshot) {
        final bytes = snapshot.data;
        if (snapshot.hasError) {
          return Container();
        }
        // If we have no data
        if (bytes == null) return Container();
        // If there's data, display it as an image
        return GestureDetector(
            onTap: () {
              onTap(bytes);
            },
            child: Image.memory(bytes, fit: BoxFit.cover,gaplessPlayback: true,));
      },
    );
  }
}

class DetailsPage extends StatefulWidget {
  final AssetEntity asset;
  final Uint8List bytes;

  const DetailsPage({Key? key, required this.asset, required this.bytes})
      : super(key: key);

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

class _DetailsPageState extends State<DetailsPage> {
  late ImageProvider _imageProvider;

  Future<void> loadFile() async {
    try {
      File? file = await widget.asset.file;
      if (file == null) return;

      _imageProvider = Image.file(file).image;
      setState(() {});
    } catch (e) {
      print("error to load file : " + e.toString());
    }
  }

  @override
  void initState() {
    _imageProvider = Image.memory(widget.bytes).image;
    loadFile();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    double ratio = widget.asset.height / widget.asset.width;
    return Scaffold(
      body: Stack(
        children: [
          Hero(
            createRectTween: (Rect? begin, Rect? end) {
              RectTween _rectTween = RectTween(begin: begin, end: end);
              return _rectTween;
            },
            tag: widget.asset.id,
            child: Center(
              child: Image(image: _imageProvider,
                  width: MediaQuery.of(context).size.width,
                  height: MediaQuery.of(context).size.height*ratio,
                  gaplessPlayback: true),
            ),
          ),
          const SafeArea(
              child: Padding(
                padding: EdgeInsets.all(8.0),
                child: BackButton(),
              )),
        ],
      ),
    );
  }
}

我已經在我的項目的其他地方實現了英雄動畫,但我沒有像這里那樣得到這種行為。

為什么會發生這種情況以及如何糾正這種情況?

添加

transitionOnUserGestures: true,

到當前和下一頁中的Hero()小部件

發生這種情況是因為圖像縮略圖的 Hero 包裝了 FutureBuilder,然后在動畫期間再次觸發未來,包裝了 Image 而不是 FutureBuilder。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM