[英]Flutter: custom transition (PageRouteBuilder) disables Hero animations
[英]Flutter use Hero transition between Custom Painter
想要我想要
您好,我想在我的基于头脑风暴应用程序的应用程序中实现一个 function。
我所做的
这是我的申请
我有一个轮子,它是第一页,当我单击其中一个“球”时,它会打开并显示第二页。
我的问题
我看不到如何像示例应用程序那样为过渡设置动画。 我必须使用“英雄”过渡,但我不知道如何在自定义画家中使用它?
我用了 2 页来做我的球开场,因为我希望能够使用我的 android 后退按钮到 go 后退。
代码
这是我的Views
:
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
import 'package:touchable/touchable.dart';
class FirstView extends StatelessWidget {
@override
Widget build(BuildContext context) {
var controller = Provider.of<CompteurProvider>(context);
return SafeArea(
child: Scaffold(
body: LayoutBuilder(
builder: (context, constraints){
return Column(
children: [
Hero(
tag: "Hero",
child: ChangeNotifierProvider<CompteurProvider>(
create: (context) => CompteurProvider(),
child: CanvasTouchDetector(
builder: (context) {
return CustomPaint(
size: Size(constraints.maxWidth, constraints.maxHeight),
painter: AreasPainter(
context,
controller.areas,
),
);
}
),
),
),
],
);
},
)
),
);
}
}
class SecondView extends StatelessWidget {
@override
Widget build(BuildContext context) {
var controller = Provider.of<CompteurProvider>(context);
return SafeArea(
child: Scaffold(
body: LayoutBuilder(
builder: (context, constraints){
return Column(
children: [
Hero(
tag: "Hero",
child: ChangeNotifierProvider<CompteurProvider>(
create: (context) => CompteurProvider(),
child: CanvasTouchDetector(
builder: (context) {
return CustomPaint(
size: Size(constraints.maxWidth, constraints.maxHeight),
painter: SubAreasPainter(
context,
controller.area!,
),
);
}
),
),
),
],
);
},
)
),
);
}
}
这是我的Painter
:
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:touchable/touchable.dart';
import 'package:provider/provider.dart';
class AreasPainter extends CustomPainter
{
final List<AreaEntity> areas;
final BuildContext context;
final CompteurProvider controller;
final int dotsPerRing;
AreasPainter(this.context, this.areas)
: dotsPerRing = areas.length,
controller = Provider.of<CompteurProvider>(context);
final double dotRadius = 40;
@override
void paint(Canvas canvas, Size size) {
TouchyCanvas touchyCanvas = TouchyCanvas(context,canvas);
// General variable
final Offset centerOffset = Offset(size.width / 2, size.height / 2);
final double centerCircleRadius = size.height / 2 - dotRadius - 10;
final double betweenAngle = 2 * pi / dotsPerRing;
// Center circle ----------------------------------------------------------
drawCenterCircle(
canvas: canvas,
centerOffset: controller.circlePosition ?? centerOffset,
radius: centerCircleRadius,
);
// Big Circle ------------------------------------------------------------
drawBigCircle(
canvas: canvas,
centerOffset: centerOffset,
radius: centerCircleRadius,
);
// Balls around ----------------------------------------------------------
areas.forEach((area) {
drawBall(
canvas: canvas,
touchyCanvas : touchyCanvas,
centerOffset : centerOffset,
offsetPositionOnCircle : getPositionOnCircle(betweenAngle: betweenAngle, id: area.id, radius: centerCircleRadius),
subValues: area.subAreas,
name: area.text,
dotRadius: dotRadius,
onTapAction: (){
print(area.text);
Navigator.pushNamed(context, RouterName.kTest2, arguments: area);
}
);
});
}
void drawCenterCircle({
required Canvas canvas,
required Offset centerOffset,
required double radius
}){
// -------------------------------------------------------------------------
Paint outCirclePaint = Paint()
..color = AppColors.kcolor_bleu
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = 2;
canvas.drawCircle(centerOffset, 20, outCirclePaint);
// -------------------------------------------------------------------------
Paint inCirclePaint = Paint()
..color = AppColors.kcolor_bleu
..strokeCap = StrokeCap.round;
canvas.drawCircle(centerOffset, 5, inCirclePaint);
// -------------------------------------------------------------------------
final text = measureText(text: "Areas", style: TextStyle(color: AppColors.kcolor_bleu, fontSize: 10));
text.paint(canvas, centerOffset - Offset(text.width / 2, -25));
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
class SubAreasPainter extends CustomPainter
{
final AreaEntity area;
final BuildContext context;
final CompteurProvider controller;
final int dotsPerRing;
SubAreasPainter(this.context, this.area)
: dotsPerRing = area.subAreas.length,
controller = Provider.of<CompteurProvider>(context);
final double dotRadius = 40;
@override
void paint(Canvas canvas, Size size) {
TouchyCanvas touchyCanvas = TouchyCanvas(context,canvas);
// General variable
final Offset centerOffset = Offset(size.width / 2, size.height / 2);
final double centerCircleRadius = size.height / 2 - dotRadius - 10;
final double betweenAngle = 2 * pi / dotsPerRing;
// Center circle ----------------------------------------------------------
drawBall(
canvas: canvas,
touchyCanvas : touchyCanvas,
centerOffset : centerOffset,
subValues: area.subAreas,
name: area.text,
dotRadius: dotRadius,
onTapAction: (){}
);
// Big Circle ------------------------------------------------------------
drawBigCircle(
canvas: canvas,
centerOffset: centerOffset,
radius: centerCircleRadius,
);
// Balls around ----------------------------------------------------------
area.subAreas.forEach((subArea) {
drawBall(
canvas: canvas,
touchyCanvas : touchyCanvas,
centerOffset : centerOffset,
offsetPositionOnCircle : getPositionOnCircle(betweenAngle: betweenAngle, id: subArea.id, radius: centerCircleRadius),
subValues: area.subAreas,
name: subArea.text,
dotRadius: dotRadius,
onTapAction: (){
Navigator.pop(context);
}
);
});
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
Offset getPositionOnCircle({
required double betweenAngle,
required int id,
required double radius
}){
double angleFromStart = betweenAngle * id;
return Offset(radius * cos(angleFromStart), radius * sin(angleFromStart));
}
TextPainter measureText({
required String text,
TextStyle? style
})
{
final textSpan = TextSpan(text: text, style: style != null ? style : TextStyle(color: Colors.white));
final textPainter = TextPainter(text: textSpan, textDirection: TextDirection.ltr);
textPainter.layout(minWidth: 0, maxWidth: double.maxFinite);
return textPainter;
}
drawBall({
required Canvas canvas,
required TouchyCanvas touchyCanvas,
required Offset centerOffset,
Offset offsetPositionOnCircle = const Offset(0,0),
required List subValues,
required String name,
required dotRadius,
Function? onTapAction
}){
// Dot background --------------------------------------------------------
Paint dotBackgroundPaint = Paint()
..color = AppColors.kBg_dark;
touchyCanvas.drawCircle(centerOffset + offsetPositionOnCircle, dotRadius + 4, dotBackgroundPaint);
// Dot -------------------------------------------------------------------
Paint dotPaint = Paint()
..color = AppColors.kBg_light
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = 2;
touchyCanvas.drawCircle(centerOffset + offsetPositionOnCircle, dotRadius, dotPaint);
// Text ------------------------------------------------------------------
final textSize = measureText(
text: name,
style: TextStyle(
color: AppColors.kFont_grey,
fontSize: 8.0,
fontWeight: FontWeight.bold,
)
);
final Offset offsetCenterText = Offset(- textSize.width / 2.0 , - textSize.height / 2.0);
textSize.paint(canvas, centerOffset + offsetPositionOnCircle + offsetCenterText);
// Touch area ----------------------------------------------------------------
Paint toucheAreaPaint = Paint()
..color = Colors.transparent;
touchyCanvas.drawCircle(centerOffset + offsetPositionOnCircle, dotRadius, toucheAreaPaint,
onTapDown: (t) {
onTapAction!();
}
);
}
void drawBigCircle({
required Canvas canvas,
required Offset centerOffset,
required double radius
}){
Paint defaultCirclePaint = Paint()
..color = AppColors.kBg_normal.withOpacity(1)
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = 3;
canvas.drawCircle(centerOffset, radius, defaultCirclePaint);
}
provider
:
class CompteurProvider with ChangeNotifier {
// Variables
// ---------------------------------------------------------------------------
Future? dataToLoad;
late List<AreaEntity> areas = [];
late double centerRingSize;
late AreaEntity? area;
// Constructor
// ---------------------------------------------------------------------------
CompteurProvider({
this.area
}){
_initialise();
}
// Initialisation
// ---------------------------------------------------------------------------
Future _initialise() async
{
dataToLoad = await loadingData();
notifyListeners();
}
Future loadingData() async
{
centerRingSize = 20;
areas.add(AreaEntity(
id: 0,
text: "AREA 1",
subAreas : [
SubAreaEntity(id: 0, text: "E1"),
SubAreaEntity(id: 1, text: "E2")
]
));
areas.add(AreaEntity(
id: 1,
text: "AREA 2",
subAreas : []
));
areas.add(AreaEntity(
id: 2,
text: "AREA 3",
subAreas : []
));
areas.add(AreaEntity(
id: 3,
text: "AREA 4",
subAreas : []
));
areas.add(AreaEntity(
id: 4,
text: "AREA 5",
subAreas : []
));
if(area != null){
area = area;
}
notifyListeners();
}
}
AreaEntity
:
import 'package:equatable/equatable.dart';
class AreaEntity extends Equatable{
int id;
String text;
List<SubAreaEntity> subAreas;
AreaEntity({
required this.id,
required this.text,
required this.subAreas,
});
@override
List<Object> get props{
return [
id,
text,
subAreas
];
}
Map<String, dynamic> toJson() => {
"id" : id,
"text" : text,
"subAreas" : subAreas,
};
}
class SubAreaEntity extends Equatable{
int id;
String text;
SubAreaEntity({
required this.id,
required this.text,
});
@override
List<Object> get props{
return [
id,
text,
];
}
Map<String, dynamic> toJson() => {
"id" : id,
"text" : text,
};
}
任何关于实现这一目标的最佳方法的指导将不胜感激。
这是示例 static 代码 - 使用 AnimatedPositioned 和 AnimatedContainer,我在单个文件 class 中为单个文件中的所有组件编写代码,借助它您可以动态创建,这只是一个想法。
import 'package:flutter/material.dart';
class GlobeExample extends StatefulWidget {
const GlobeExample({Key? key}) : super(key: key);
@override
State<GlobeExample> createState() => _GlobeExampleState();
}
class _GlobeExampleState extends State<GlobeExample> {
bool isExpand = false;
double sizeValue = 75.0, lineSize = 0.0;
int lineDuration = 1000;
@override
Widget build(BuildContext context) {
final Size size = MediaQuery.of(context).size;
return Scaffold(
backgroundColor: Colors.white,
body: SizedBox(
height: size.height,
width: size.width,
child: GestureDetector(
onTap: () => setState(() {
isExpand = !isExpand;
isExpand == true ? sizeValue = 100.0 : sizeValue = 75.0;
isExpand == true ? lineDuration = 800 : lineDuration = 1000;
isExpand == true ? lineSize = 400.0 : lineSize = 0.0;
}),
child: Stack(
alignment: Alignment.center,
children: [
AnimatedContainer(
duration: const Duration(seconds: 1),
height: isExpand == true ? 400.0 : 250.0,
width: isExpand == true ? 400.0 : 250.0,
decoration: BoxDecoration(
border: Border.all(width: 2.5, color: Colors.blueGrey),
borderRadius: BorderRadius.circular(250.0),
),
),
Container(
height: 100.0,
width: 100.0,
decoration: BoxDecoration(
color: Colors.blueGrey,
borderRadius: BorderRadius.circular(100.0),
),
),
AnimatedContainer(
duration: Duration(milliseconds: lineDuration),
height: lineSize,
width: 2.0,
decoration: BoxDecoration(
border: Border.all(width: 2.5, color: Colors.blueGrey),
),
),
AnimatedContainer(
duration: Duration(milliseconds: lineDuration),
height: 2.0,
width: lineSize,
decoration: BoxDecoration(
border: Border.all(width: 2.5, color: Colors.blueGrey),
),
),
Transform.rotate(
angle: 0.77,
child: AnimatedContainer(
duration: Duration(milliseconds: lineDuration),
height: lineSize,
width: 2.0,
decoration: BoxDecoration(
border: Border.all(width: 2.5, color: Colors.blueGrey),
),
),
),
Transform.rotate(
angle: -0.77,
child: AnimatedContainer(
duration: Duration(milliseconds: lineDuration),
height: lineSize,
width: 2.0,
decoration: BoxDecoration(
border: Border.all(width: 2.5, color: Colors.blueGrey),
),
),
),
AnimatedPositioned(
duration: const Duration(seconds: 1),
top: isExpand == true ? 200.0 : 290.0,
child: AnimatedContainer(
duration: const Duration(seconds: 1),
height: sizeValue,
width: sizeValue,
decoration: BoxDecoration(
color: Colors.blueGrey,
borderRadius: BorderRadius.circular(sizeValue),
),
),
),
AnimatedPositioned(
duration: const Duration(seconds: 1),
bottom: isExpand == true ? 200.0 : 290.0,
child: AnimatedContainer(
duration: const Duration(seconds: 1),
height: sizeValue,
width: sizeValue,
decoration: BoxDecoration(
color: Colors.blueGrey,
borderRadius: BorderRadius.circular(sizeValue),
),
),
),
AnimatedPositioned(
duration: const Duration(seconds: 1),
right: isExpand == true ? -38.0 : 50.0,
child: AnimatedContainer(
duration: const Duration(seconds: 1),
height: sizeValue,
width: sizeValue,
decoration: BoxDecoration(
color: Colors.blueGrey,
borderRadius: BorderRadius.circular(sizeValue),
),
),
),
AnimatedPositioned(
duration: const Duration(seconds: 1),
left: isExpand == true ? -38.0 : 50.0,
child: AnimatedContainer(
duration: const Duration(seconds: 1),
height: sizeValue,
width: sizeValue,
decoration: BoxDecoration(
color: Colors.blueGrey,
borderRadius: BorderRadius.circular(sizeValue),
),
),
),
AnimatedPositioned(
duration: const Duration(seconds: 1),
bottom: isExpand == true ? 256.0 : 324.0,
right: isExpand == true ? 20.0 : 80.0,
child: AnimatedContainer(
duration: const Duration(seconds: 1),
height: sizeValue,
width: sizeValue,
decoration: BoxDecoration(
color: Colors.blueGrey,
borderRadius: BorderRadius.circular(sizeValue),
),
),
),
AnimatedPositioned(
duration: const Duration(seconds: 1),
bottom: isExpand == true ? 256.0 : 324.0,
left: isExpand == true ? 20.0 : 80.0,
child: AnimatedContainer(
duration: const Duration(seconds: 1),
height: sizeValue,
width: sizeValue,
decoration: BoxDecoration(
color: Colors.blueGrey,
borderRadius: BorderRadius.circular(sizeValue),
),
),
),
AnimatedPositioned(
duration: const Duration(seconds: 1),
top: isExpand == true ? 256.0 : 324.0,
right: isExpand == true ? 20.0 : 80.0,
child: AnimatedContainer(
duration: const Duration(seconds: 1),
height: sizeValue,
width: sizeValue,
decoration: BoxDecoration(
color: Colors.blueGrey,
borderRadius: BorderRadius.circular(sizeValue),
),
),
),
AnimatedPositioned(
duration: const Duration(seconds: 1),
top: isExpand == true ? 256.0 : 324.0,
left: isExpand == true ? 20.0 : 80.0,
child: AnimatedContainer(
duration: const Duration(seconds: 1),
height: sizeValue,
width: sizeValue,
decoration: BoxDecoration(
color: Colors.blueGrey,
borderRadius: BorderRadius.circular(sizeValue),
),
),
),
],
),
),
),
);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.