Flutter:ChangeNotiver子类持有ScrollController是不是不好的做法?

q5lcpyga  于 2023-05-19  发布在  Flutter
关注(0)|答案(2)|浏览(206)

我认为在控制ListView滚动的应用中使用StatefulWidget实现以下内容是非常正常的。

//sample(a)
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(
    MaterialApp(
      home: HomePage(),
    ),
  );
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: [
            ElevatedButton(
              child: Text('Go to ListViewPage'),
              onPressed: () {
                Navigator.of(context).push(
                  MaterialPageRoute(
                    builder: (context) =>
                        ChangeNotifierProvider<ListViewPageController>(
                      create: (_) => ListViewPageController(),
                      child: ListViewPage(),
                    ),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

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

  @override
  State<ListViewPage> createState() => _ListViewPageState();
}

class _ListViewPageState extends State<ListViewPage> {
  final ScrollController sc = ScrollController();

  @override
  void dispose() {
    sc.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: ListView.builder(
          controller: sc,
          itemCount: 100,
          itemBuilder: (context, index) {
            return ListTile(title: Text('$index'));
          }),
    );
  }
}

另一方面,我认为也可以考虑以下使用ChangeNotifier/Provider(不是Riverpod)管理ListViewPage滚动而不使用StatefulWidget的实现。实际执行本身是没有问题的。

////sample(b)
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class ListViewPageController extends ChangeNotifier {
  ScrollController sc = ScrollController();
}

void main() {
  runApp(
    MaterialApp(
      home: HomePage(),
    ),
  );
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: [
            ElevatedButton(
              child: Text('Go to ListViewPage'),
              onPressed: () {
                Navigator.of(context).push(
                  MaterialPageRoute(
                    builder: (context) =>
                        ChangeNotifierProvider<ListViewPageController>(
                          create: (_) => ListViewPageController(),
                          child: ListViewPage(),
                        ),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

class ListViewPage extends StatelessWidget {
  const ListViewPage({Key? key}) : super(key: key);

  //↓Is this part mandatory?
  /*
  late final ScrollController sc = context.read<ListViewPageController>().sc;

  @override
  void dispose() {
    sc.dispose();
    super.dispose();
  }
*/

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: ListView.builder(
          controller: context.read<ListViewPageController>().sc,
          itemCount: 100,
          itemBuilder: (context, index) {
            return ListTile(title: Text('$index'));
          }),
    );
  }
}

问题1示例(B)与示例(a)具有相同的行为,但由于ListViewPage是StatelessWidget,因此不会调用sc.dispose。

我目前的理解是,当我们从ListViewPage返回到HomePage时,ListViewPageController将被销毁,但ScrollController也会自动销毁吗?难道我不应该自己写关于sc.dispose的代码吗?
或者我必须让ListViewPage成为一个StatefulWidget,然后自己编写代码来调用state.dispose中的sc.dispose?

Question 2最近,我听到了下面的说法(解释)。

ChangeNtifier(或StateNotfier)的子类不应依赖于任何UI代码。Flutter的ScrollController/PageController/TextEditingController/Forms等是Flutter SDK中的类(即UI代码)。所以在这个例子中ListViewPageController不应该有(依赖于)ScrollController。“
是这样吗?
我还看到了另一个使用Scrollable.ensureVisible来控制滚动的示例代码,但是由于这个Scrollable类也是UI代码,所以在ChangeNotifier子类中使用它是不是一个不好的做法,如下所示?(在这种情况下,每个ListTile都应该有一个GlobalKey。
就我个人而言,我认为如果ChangeNotifier子类包含ScrollController、PageController等,会更容易理解。在Flutter中开发时,这个理论有多重要?

class ListViewPageController extends ChangeNotifier {
  ScrollController sc = ScrollController();
  
  scrollRequest(int scrollIndex) {
    Scrollable.ensureVisible(keyList[scrollIndex - 100].currentContext!,
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeOut,
        alignment: 0.5,
        alignmentPolicy: ScrollPositionAlignmentPolicy.explicit);
  }

}
bihw5rsg

bihw5rsg1#

在样本B中,sc是< ScrollController >另一个obj中的一个obj< ListViewPageController >,假设它是lc。
当lc(disposed)->被删除时,sc(disposed)->被删除。
Q2.在ListViewPageController中按住ScrollController可能会在ListViewPage仍处于活动状态时意外释放ListViewPageController时导致错误。
如果你想访问ScrollController,尝试将其注入ListViewPageController将是一个更好的做法。

svdrlsy4

svdrlsy42#

你也可以在ListViewPageController中调用dispose方法,并在那里dispose ScrollController sc,我也使用这种方法,但我不确定它是否正确。

class ListViewPageController extends ChangeNotifier {
 ScrollController sc = ScrollController();
 @override
void dispose(){
 sc.dispose();
}
}

相关问题