最近开始学习riverpod和provider作为状态管理方法。因此,我想重新构建当前的应用程序。
我的问题
在我的应用程序中,我有一个列表(ListView.builder),它显示多个卡。当一张卡片被点击(手势检测器),它应该被翻转。这与之前的状态管理(setState())一起工作,没有任何问题。当使用riverpod时,当点击一张卡片时,ListView中的所有卡片现在都会翻转。
下面是我的代码: 提供者
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@immutable
class TestMovieCard {
const TestMovieCard({
required this.id,
required this.title,
});
final String id;
final String title;
}
class MoviesNotifier extends StateNotifier<List<TestMovieCard>> {
MoviesNotifier()
: super([
const TestMovieCard(id: "1", title: "Im Westen nichts Neues"),
const TestMovieCard(
id: "2", title: "Star Wars Das Erwachen der Macht")
]);
double _flipAngle = 0;
double get flipAngle => _flipAngle;
void flipMovieCard() {
_flipAngle = (_flipAngle + pi) % (2 * pi);
state = [...state];
}
bool checkIfCardIsFront(double animationValue) {
if (animationValue >= (pi / 2)) {
return false;
} else {
return true;
}
}
}
final movieCardProvider =
StateNotifierProvider<MoviesNotifier, List<TestMovieCard>>((ref) {
return MoviesNotifier();
});
部件
class Test extends ConsumerWidget {
const Test({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
Size size = MediaQuery.of(context).size;
List<TestMovieCard> movieCards = ref.watch(movieCardProvider);
return DefaultTabController(
length: 2,
child: Scaffold(
body: Container(
padding: const EdgeInsets.all(defaultPadding),
child: Column(
children: [
SizedBox(
height: size.height * 0.05,
),
Expanded(
child: SizedBox(
width: double.infinity,
child: TabBarView(children: [
MediaQuery.removePadding(
removeTop: true,
context: context,
child: ListView.builder(
itemCount: movieCards.length,
itemBuilder: (context, index) {
return GestureDetector(
// key: Key(movieCards[widget.listIndex].id),
onTap: () {
ref
.watch(movieCardProvider.notifier)
.flipMovieCard();
},
child: TweenAnimationBuilder(
tween: Tween<double>(
begin: 0,
end: ref
.watch(
movieCardProvider.notifier)
.flipAngle),
duration: const Duration(seconds: 1),
builder: (BuildContext context,
double val, __) {
return (Transform(
alignment: Alignment.center,
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(val),
child: ref
.watch(movieCardProvider
.notifier)
.checkIfCardIsFront(val)
? Card(
elevation: 5,
shape: const RoundedRectangleBorder(
borderRadius:
BorderRadius.all(
Radius.circular(
20.0))),
child: Container(
height: size.height *
0.25,
decoration:
const BoxDecoration(
borderRadius:
BorderRadius.all(
Radius.circular(
20.0)),
),
child: const Center(
child:
Text("Front"),
)))
: Transform(
alignment:
Alignment.center,
transform:
Matrix4.identity()
..rotateY(pi),
child: Card(
elevation: 5,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius
.all(Radius
.circular(
20.0))),
child: Container(
height:
size.height *
0.25,
width:
double.infinity,
decoration:
const BoxDecoration(
borderRadius:
BorderRadius
.all(
Radius.circular(
20.0),
),
),
child: const Center(
child:
Text("Back"),
),
)),
)));
}),
);
})),
Placeholder()
])),
)
],
)),
bottomNavigationBar: const CustomBottomNavigationBar(),
),
);
}
}
我希望我把我的观点讲清楚了。提前谢谢你。
我已经尝试在几个地方实现消费者,但是所有列表项的状态总是被重新加载。
编辑:基于Axel的帮助的新Provider代码
@immutable
class TestMovieCard {
TestMovieCard({
required this.id,
required this.title,
});
final String id;
final String title;
double flipAngle = 0;
void flip() {
flipAngle = (flipAngle + pi) % (2 * pi);
}
}
class MoviesNotifier extends StateNotifier<List<TestMovieCard>> {
MoviesNotifier()
: super([
TestMovieCard(id: "1", title: "Im Westen nichts Neues"),
TestMovieCard(
id: "2",
title: "Star Wars Das Erwachen der Macht",
)
]);
void flipMovieCard(String idToFlip) {
state = [
...state.where((x) => x.id != idToFlip),
...state.where((x) => x.id == idToFlip).flip()
];
}
bool checkIfCardIsFront(double animationValue) {
if (animationValue >= (pi / 2)) {
return false;
} else {
return true;
}
}
}
final movieCardProvider =
StateNotifierProvider<MoviesNotifier, List<TestMovieCard>>((ref) {
return MoviesNotifier();
});
你应该在你的TestMovieCard
中有_flipAngle
属性,这样你就可以区分哪些牌必须翻转,哪些牌不可以。
然后,一旦调用flipMovieCard
,传递卡片索引只翻转所选卡片。代码应该像这样:
void flipMovieCard(int index) {
state[index].flipAngle = (state[index].flipAngle + pi) % (2 * pi);
}
编辑post OP的评论:
小部件没有自动重建的原因是您需要重新创建整个列表。
您可以在TestMovieCard
类中实现flip
方法,然后执行以下操作:
TestMovieCard flippedCard = state.where((x) => x.id == idToFlip).first;
flippedCard.flip();
state = [...state.where((x) => x.id != idToFlip), flippedCard];
代替:
state[index].flipAngle = (state[index].flipAngle + pi) % (2 * pi);