将 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();
});