将 ChangeNotifier 实现为 StateNotifier
Implementing ChangeNotifier to StateNotifier
我已将 ChangeNotifier 代码重写为 StateNotifier,但问题是当您按下喜欢或不喜欢学生行中的学生按钮时,它不会重建。我希望 ref.watch() 监听 likedStudentsNotifier 的状态,并且在发生变化时使 StudentRow 小部件构建重建。但事实并非如此。为什么会这样?要解决 StudentRow class 我可以使用 ConsumerStatefulWidget 和 setState 而不是 ConsumerWidget 但我想知道它是如何工作的?
ChangeNotifier:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const ProviderScope(child: Schoolapp()));
}
class Schoolapp extends StatelessWidget {
const Schoolapp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'School App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(title: 'School App Main Page'),
);
}
}
class HomePage extends ConsumerWidget {
const HomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
child: Text('${ref.watch(studentsProvider).studentsList.length} Students'),
onPressed: () {
_runStudentsPage(context);
},
),
],
),
),
);
}
void _runStudentsPage(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return const StudentsPage();
},
));
}
}
class StudentsPage extends ConsumerWidget {
const StudentsPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('Students')),
body: Column(
children: [
PhysicalModel(
color: Colors.white,
elevation: 10,
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 32.0, horizontal: 32.0),
child: Text(
'${ref.watch(studentsProvider).studentsList.length} Students'
),
),
),
),
Expanded(
child: ListView.separated(
itemBuilder: (context, index) => StudentRow(
ref.watch(studentsProvider).studentsList[index],
),
separatorBuilder: (context, index) => const Divider(),
itemCount: ref.watch(studentsProvider).studentsList.length,
),
),
],
),
);
}
}
class StudentRow extends ConsumerWidget {
final StudentModel student;
const StudentRow(this.student, {
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
bool isLiked = ref.watch(studentsProvider).isLiked(student);
return ListTile(
title: Text(student.name + ' ' + student.surName),
leading: IntrinsicWidth(child: Center(child: Text(student.gender == 'Female' ? '' : ''))),
trailing: IconButton(
onPressed: () {
ref.read(studentsProvider).like(student, !isLiked);
},
icon: Icon(isLiked ? Icons.favorite : Icons.favorite_border),
),
);
}
}
class StudentsRepository extends ChangeNotifier {
final studentsList = [
StudentModel('Aziz', 'Sancar', 18, 'Male'),
StudentModel('Iggy', 'Azalea', 20, 'Female'),
StudentModel('Madonna', 'Rrww', 22, 'Female'),
];
final Set<StudentModel> likedStudents = {};
void like(StudentModel student, bool isLiked) {
if (isLiked == true) {
likedStudents.add(student);
} else {
likedStudents.remove(student);
}
notifyListeners();
}
bool isLiked(StudentModel student) {
return likedStudents.contains(student);
}
}
final studentsProvider = ChangeNotifierProvider((ref) {
return StudentsRepository();
});
class StudentModel {
String name;
String surName;
int age;
String gender;
StudentModel(this.name, this.surName, this.age, this.gender);
}
StateNotifier:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const ProviderScope(child: const Schoolapp()));
}
class Schoolapp extends StatelessWidget {
const Schoolapp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'School App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(title: 'School App Main Page'),
);
}
}
class HomePage extends ConsumerWidget {
const HomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
child: Text(
'${ref.watch(studentsProvider.notifier).state.length} Students'),
onPressed: () {
_runStudentsPage(context);
},
),
],
),
),
);
}
void _runStudentsPage(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return StudentsPage();
},
));
}
}
class StudentsPage extends ConsumerWidget {
const StudentsPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('Students Page')),
body: Column(
children: [
PhysicalModel(
color: Colors.white,
elevation: 10,
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 32.0, horizontal: 32.0),
child: Text(
'${ref.watch(studentsProvider.notifier).state.length} Students'),
),
),
),
Expanded(
child: ListView.separated(
itemBuilder: (context, index) => StudentRow(
ref.watch(studentsProvider.notifier).state[index],
),
separatorBuilder: (context, index) => const Divider(),
itemCount: ref.watch(studentsProvider.notifier).state.length,
),
),
],
),
);
}
}
class StudentRow extends ConsumerWidget {
final StudentModel student;
const StudentRow(
this.student, {
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
bool isStudentLiked =
ref.watch(likedStudentsProvider.notifier).isLiked(student);
return ListTile(
title: Text(student.name + ' ' + student.surName),
leading: IntrinsicWidth(
child: Center(
child: Text(student.gender == 'Female' ? '' : ''))),
trailing: IconButton(
onPressed: () {
ref
.read(likedStudentsProvider.notifier)
.likeStudent(student, !isStudentLiked);
},
icon: Icon(isStudentLiked ? Icons.favorite : Icons.favorite_border),
),
);
}
}
class StudentsRepository extends StateNotifier<List<StudentModel>> {
StudentsRepository() : super([]);
addStudent(StudentModel studentToAdd) {
return state = [...state, studentToAdd];
}
@override
final state = [
StudentModel('Aziz', 'Sancar', 18, 'Male'),
StudentModel('Iggy', 'Azalea', 20, 'Female'),
StudentModel('Madonna', 'Rrww', 22, 'Female'),
];
}
class likedStudentsNotifier extends StateNotifier<Set<StudentModel>> {
likedStudentsNotifier() : super({});
Set<StudentModel> state = {};
void likeStudent(StudentModel studentToAdd, bool isLiked) {
if (isLiked == true) {
state = {...state, studentToAdd};
} else {
state = {
for (final student in state)
if (student != studentToAdd) student,
};
}
}
isLiked(StudentModel student) {
return state.contains(student);
}
}
class StudentModel {
String name;
String surName;
int age;
String gender;
StudentModel(this.name, this.surName, this.age, this.gender);
}
final likedStudentsProvider =
StateNotifierProvider<likedStudentsNotifier, Set<StudentModel>>((ref) {
return likedStudentsNotifier();
});
final studentsProvider = StateNotifierProvider<StudentsRepository, List>((ref) {
return StudentsRepository();
});
你的问题是阅读 notifier
的方式不对 ref.watch(provider.notifier).state
vs ref.watch(provider)
,左边是不好的。我已将您的代码重写为更清晰,并且在如何处理 StateNotifier
中的状态方面有更好的实践。您可以将我的代码与您的代码进行比较,看看有什么问题。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const ProviderScope(child: Schoolapp()));
}
class Schoolapp extends StatelessWidget {
const Schoolapp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'School App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(title: 'School App Main Page'),
);
}
}
class HomePage extends ConsumerWidget {
const HomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
child: Text(
'${ref.watch(studentsProvider).length} Students',
),
onPressed: () {
_runStudentsPage(context);
},
),
],
),
),
);
}
void _runStudentsPage(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return const StudentsPage();
},
));
}
}
class StudentsPage extends ConsumerWidget {
const StudentsPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('Students Page')),
body: Column(
children: [
PhysicalModel(
color: Colors.white,
elevation: 10,
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 32.0, horizontal: 32.0),
child: Text('${ref.watch(studentsProvider).length} Students'),
),
),
),
Expanded(
child: ListView.separated(
itemBuilder: (context, index) => StudentRow(
ref.watch(studentsProvider)[index],
),
separatorBuilder: (context, index) => const Divider(),
itemCount: ref.read(studentsProvider).length,
),
),
],
),
);
}
}
class StudentRow extends ConsumerWidget {
final StudentModel student;
const StudentRow(
this.student, {
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return ListTile(
title: Text(student.name + ' ' + student.surName),
leading: IntrinsicWidth(
child: Center(
child: Text(student.gender == 'Female' ? '' : ''),
),
),
trailing: IconButton(
onPressed: () {
ref.read(studentsProvider.notifier).like(student);
},
icon: Icon(student.isLiked ? Icons.favorite : Icons.favorite_border),
),
);
}
}
class StudentsRepository extends StateNotifier<List<StudentModel>> {
StudentsRepository()
: super([
StudentModel('Aziz', 'Sancar', 18, 'Male', false),
StudentModel('Iggy', 'Azalea', 20, 'Female', false),
StudentModel('Madonna', 'Rrww', 22, 'Female', false),
]);
void add(StudentModel student) {
state = [...state, student];
}
void like(StudentModel student) {
state = [
for (var element in state)
if (element == student)
StudentModel(
element.name,
element.surName,
element.age,
element.gender,
element.isLiked = !element.isLiked,
)
else
element
];
}
void remove(StudentModel student) {
state = state.where((e) => e != student).toList();
}
}
class StudentModel {
String name;
String surName;
int age;
String gender;
bool isLiked;
StudentModel(this.name, this.surName, this.age, this.gender, this.isLiked);
@override
bool operator ==(covariant StudentModel model) {
return model.name == name &&
model.surName == surName &&
model.age == age &&
model.gender == gender &&
model.isLiked == isLiked;
}
@override
int get hashCode =>
name.hashCode ^
surName.hashCode ^
age.hashCode ^
gender.hashCode ^
isLiked.hashCode;
}
final studentsProvider = StateNotifierProvider<StudentsRepository, List>((ref) {
return StudentsRepository();
});
我已将 ChangeNotifier 代码重写为 StateNotifier,但问题是当您按下喜欢或不喜欢学生行中的学生按钮时,它不会重建。我希望 ref.watch() 监听 likedStudentsNotifier 的状态,并且在发生变化时使 StudentRow 小部件构建重建。但事实并非如此。为什么会这样?要解决 StudentRow class 我可以使用 ConsumerStatefulWidget 和 setState 而不是 ConsumerWidget 但我想知道它是如何工作的?
ChangeNotifier:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const ProviderScope(child: Schoolapp()));
}
class Schoolapp extends StatelessWidget {
const Schoolapp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'School App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(title: 'School App Main Page'),
);
}
}
class HomePage extends ConsumerWidget {
const HomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
child: Text('${ref.watch(studentsProvider).studentsList.length} Students'),
onPressed: () {
_runStudentsPage(context);
},
),
],
),
),
);
}
void _runStudentsPage(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return const StudentsPage();
},
));
}
}
class StudentsPage extends ConsumerWidget {
const StudentsPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('Students')),
body: Column(
children: [
PhysicalModel(
color: Colors.white,
elevation: 10,
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 32.0, horizontal: 32.0),
child: Text(
'${ref.watch(studentsProvider).studentsList.length} Students'
),
),
),
),
Expanded(
child: ListView.separated(
itemBuilder: (context, index) => StudentRow(
ref.watch(studentsProvider).studentsList[index],
),
separatorBuilder: (context, index) => const Divider(),
itemCount: ref.watch(studentsProvider).studentsList.length,
),
),
],
),
);
}
}
class StudentRow extends ConsumerWidget {
final StudentModel student;
const StudentRow(this.student, {
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
bool isLiked = ref.watch(studentsProvider).isLiked(student);
return ListTile(
title: Text(student.name + ' ' + student.surName),
leading: IntrinsicWidth(child: Center(child: Text(student.gender == 'Female' ? '' : ''))),
trailing: IconButton(
onPressed: () {
ref.read(studentsProvider).like(student, !isLiked);
},
icon: Icon(isLiked ? Icons.favorite : Icons.favorite_border),
),
);
}
}
class StudentsRepository extends ChangeNotifier {
final studentsList = [
StudentModel('Aziz', 'Sancar', 18, 'Male'),
StudentModel('Iggy', 'Azalea', 20, 'Female'),
StudentModel('Madonna', 'Rrww', 22, 'Female'),
];
final Set<StudentModel> likedStudents = {};
void like(StudentModel student, bool isLiked) {
if (isLiked == true) {
likedStudents.add(student);
} else {
likedStudents.remove(student);
}
notifyListeners();
}
bool isLiked(StudentModel student) {
return likedStudents.contains(student);
}
}
final studentsProvider = ChangeNotifierProvider((ref) {
return StudentsRepository();
});
class StudentModel {
String name;
String surName;
int age;
String gender;
StudentModel(this.name, this.surName, this.age, this.gender);
}
StateNotifier:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const ProviderScope(child: const Schoolapp()));
}
class Schoolapp extends StatelessWidget {
const Schoolapp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'School App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(title: 'School App Main Page'),
);
}
}
class HomePage extends ConsumerWidget {
const HomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
child: Text(
'${ref.watch(studentsProvider.notifier).state.length} Students'),
onPressed: () {
_runStudentsPage(context);
},
),
],
),
),
);
}
void _runStudentsPage(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return StudentsPage();
},
));
}
}
class StudentsPage extends ConsumerWidget {
const StudentsPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('Students Page')),
body: Column(
children: [
PhysicalModel(
color: Colors.white,
elevation: 10,
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 32.0, horizontal: 32.0),
child: Text(
'${ref.watch(studentsProvider.notifier).state.length} Students'),
),
),
),
Expanded(
child: ListView.separated(
itemBuilder: (context, index) => StudentRow(
ref.watch(studentsProvider.notifier).state[index],
),
separatorBuilder: (context, index) => const Divider(),
itemCount: ref.watch(studentsProvider.notifier).state.length,
),
),
],
),
);
}
}
class StudentRow extends ConsumerWidget {
final StudentModel student;
const StudentRow(
this.student, {
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
bool isStudentLiked =
ref.watch(likedStudentsProvider.notifier).isLiked(student);
return ListTile(
title: Text(student.name + ' ' + student.surName),
leading: IntrinsicWidth(
child: Center(
child: Text(student.gender == 'Female' ? '' : ''))),
trailing: IconButton(
onPressed: () {
ref
.read(likedStudentsProvider.notifier)
.likeStudent(student, !isStudentLiked);
},
icon: Icon(isStudentLiked ? Icons.favorite : Icons.favorite_border),
),
);
}
}
class StudentsRepository extends StateNotifier<List<StudentModel>> {
StudentsRepository() : super([]);
addStudent(StudentModel studentToAdd) {
return state = [...state, studentToAdd];
}
@override
final state = [
StudentModel('Aziz', 'Sancar', 18, 'Male'),
StudentModel('Iggy', 'Azalea', 20, 'Female'),
StudentModel('Madonna', 'Rrww', 22, 'Female'),
];
}
class likedStudentsNotifier extends StateNotifier<Set<StudentModel>> {
likedStudentsNotifier() : super({});
Set<StudentModel> state = {};
void likeStudent(StudentModel studentToAdd, bool isLiked) {
if (isLiked == true) {
state = {...state, studentToAdd};
} else {
state = {
for (final student in state)
if (student != studentToAdd) student,
};
}
}
isLiked(StudentModel student) {
return state.contains(student);
}
}
class StudentModel {
String name;
String surName;
int age;
String gender;
StudentModel(this.name, this.surName, this.age, this.gender);
}
final likedStudentsProvider =
StateNotifierProvider<likedStudentsNotifier, Set<StudentModel>>((ref) {
return likedStudentsNotifier();
});
final studentsProvider = StateNotifierProvider<StudentsRepository, List>((ref) {
return StudentsRepository();
});
你的问题是阅读 notifier
的方式不对 ref.watch(provider.notifier).state
vs ref.watch(provider)
,左边是不好的。我已将您的代码重写为更清晰,并且在如何处理 StateNotifier
中的状态方面有更好的实践。您可以将我的代码与您的代码进行比较,看看有什么问题。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const ProviderScope(child: Schoolapp()));
}
class Schoolapp extends StatelessWidget {
const Schoolapp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'School App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(title: 'School App Main Page'),
);
}
}
class HomePage extends ConsumerWidget {
const HomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
child: Text(
'${ref.watch(studentsProvider).length} Students',
),
onPressed: () {
_runStudentsPage(context);
},
),
],
),
),
);
}
void _runStudentsPage(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return const StudentsPage();
},
));
}
}
class StudentsPage extends ConsumerWidget {
const StudentsPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('Students Page')),
body: Column(
children: [
PhysicalModel(
color: Colors.white,
elevation: 10,
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 32.0, horizontal: 32.0),
child: Text('${ref.watch(studentsProvider).length} Students'),
),
),
),
Expanded(
child: ListView.separated(
itemBuilder: (context, index) => StudentRow(
ref.watch(studentsProvider)[index],
),
separatorBuilder: (context, index) => const Divider(),
itemCount: ref.read(studentsProvider).length,
),
),
],
),
);
}
}
class StudentRow extends ConsumerWidget {
final StudentModel student;
const StudentRow(
this.student, {
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
return ListTile(
title: Text(student.name + ' ' + student.surName),
leading: IntrinsicWidth(
child: Center(
child: Text(student.gender == 'Female' ? '' : ''),
),
),
trailing: IconButton(
onPressed: () {
ref.read(studentsProvider.notifier).like(student);
},
icon: Icon(student.isLiked ? Icons.favorite : Icons.favorite_border),
),
);
}
}
class StudentsRepository extends StateNotifier<List<StudentModel>> {
StudentsRepository()
: super([
StudentModel('Aziz', 'Sancar', 18, 'Male', false),
StudentModel('Iggy', 'Azalea', 20, 'Female', false),
StudentModel('Madonna', 'Rrww', 22, 'Female', false),
]);
void add(StudentModel student) {
state = [...state, student];
}
void like(StudentModel student) {
state = [
for (var element in state)
if (element == student)
StudentModel(
element.name,
element.surName,
element.age,
element.gender,
element.isLiked = !element.isLiked,
)
else
element
];
}
void remove(StudentModel student) {
state = state.where((e) => e != student).toList();
}
}
class StudentModel {
String name;
String surName;
int age;
String gender;
bool isLiked;
StudentModel(this.name, this.surName, this.age, this.gender, this.isLiked);
@override
bool operator ==(covariant StudentModel model) {
return model.name == name &&
model.surName == surName &&
model.age == age &&
model.gender == gender &&
model.isLiked == isLiked;
}
@override
int get hashCode =>
name.hashCode ^
surName.hashCode ^
age.hashCode ^
gender.hashCode ^
isLiked.hashCode;
}
final studentsProvider = StateNotifierProvider<StudentsRepository, List>((ref) {
return StudentsRepository();
});