fluttercandies / flutter_scrollview_observer

A widget for observing data related to the child widgets being displayed in a ScrollView. Maintainer: @LinXunFeng
https://pub.dev/packages/scrollview_observer
MIT License
438 stars 47 forks source link

请问如何在顶部加载了数据以后保持位置不变? #64

Closed yzxh24 closed 11 months ago

yzxh24 commented 11 months ago

Platforms

Android, iOS

Description

我希望用户在往上滚动到 ListView 顶部的时候加载一些历史数据,测试发现当给数组添加了新内容时列表会直接跳到最顶上,而我想要的效果是历史数据在顶部加载出来了,但是当前位置不变,继续往上滚动的时候再开始显示新加载的历史数据,比如下面的图:

Simulator Screenshot - iPhone 15 Pro - 2023-11-23 at 13 31 51 这是默认的列表。

Simulator Screenshot - iPhone 15 Pro - 2023-11-23 at 13 32 11 当我点击了右下角的 + 号按钮后依次添加了 -1、-2、-3、-4 四条数据。

我希望的效果是添加数据时当前位置不变动,即 -1、-2、-3、-4 这四条在顶部,等继续向上滚动时才出现。

My code

import 'package:flutter/material.dart';
import 'package:scrollview_observer/scrollview_observer.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const HomeTest(),
    );
  }
}

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

  @override
  State<HomeTest> createState() => _HomeTestState();
}

class _HomeTestState extends State<HomeTest> {
  List<String> list = List.generate(100, (index) => index.toString());

  late ScrollController controller;
  late ListObserverController observerController;
  late ChatScrollObserver chatScrollObserver;

  @override
  void initState() {
    super.initState();

    controller = ScrollController();
    observerController = ListObserverController(controller: controller);
    chatScrollObserver = ChatScrollObserver(observerController)
    // 超过该偏移量才会触发保持当前聊天位置的功能
      ..fixedPositionOffset = 5
      ..toRebuildScrollViewCallback = () {
        // 这里可以重建指定的滚动视图即可
        setState(() {});
      };
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListViewObserver(
        controller: observerController,
        child: ListView(
          controller: controller,
          children: list.map((e) {
            return SizedBox(
              height: 100,
              child: Center(
                child: Text(e),
              ),
            );
          }).toList()
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          chatScrollObserver.standby();
          setState(() {
            list.insert(0, '-1');
            list.insert(0, '-2');
            list.insert(0, '-3');
            list.insert(0, '-4');
          });
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

Try do it

No response

LinXunFeng commented 11 months ago

设置这个

fixedPositionOffset = -1
yzxh24 commented 11 months ago

设置这个

fixedPositionOffset = -1

我设置了一下,添加数据后依然会跳到顶部,我将列表拉到了很靠下的地方再添加数据,发现列表会跳动一下。

同时我尝试设置了 cacheJumpIndexOffset = false 还是一样的结果。

下面是我的测试代码:

import 'package:flutter/material.dart';
import 'package:scrollview_observer/scrollview_observer.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const HomeTest(),
    );
  }
}

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

  @override
  State<HomeTest> createState() => _HomeTestState();
}

class _HomeTestState extends State<HomeTest> {
  List<String> list = List.generate(100, (index) => index.toString());

  late ScrollController controller;
  late ListObserverController observerController;
  late ChatScrollObserver chatScrollObserver;

  @override
  void initState() {
    super.initState();

    controller = ScrollController();
    observerController = ListObserverController(controller: controller)..cacheJumpIndexOffset = false;
    chatScrollObserver = ChatScrollObserver(observerController)
      ..fixedPositionOffset = -1;
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListViewObserver(
        controller: observerController,
        child: ListView.builder(
          itemCount: list.length,
          physics: ChatObserverBouncingScrollPhysics(observer: chatScrollObserver),
          controller: controller,
          itemBuilder: (context, index) {
            return SizedBox(
              height: 100,
              child: Center(
                child: Text(list[index]),
              )
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          chatScrollObserver.standby();
          setState(() {
            list.insert(0, '-1');
            list.insert(0, '-2');
            list.insert(0, '-3');
            list.insert(0, '-4');
          });
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}
LinXunFeng commented 11 months ago

确实有这个问题,在 item 高度相近的情况下,更新数据并 setStateviewport 并没有更新高度,你可以先用下面的方式临时解决问题,后面我再研究一下

class _HomeTestState extends State<HomeTest> {
  ...
+  bool isUpdate = false;
  ...

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
+          Expanded(child: _buildListView()),
+          SizedBox(height: isUpdate ? 0.000001 : 0.000002),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
+          // changeCount需要跟你更新的数据量对应上
+          chatScrollObserver.standby(changeCount: 4);
+          isUpdate = !isUpdate;
          setState(() {
            list.insert(0, '-1');
            list.insert(0, '-2');
            list.insert(0, '-3');
            list.insert(0, '-4');
          });
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}
LinXunFeng commented 11 months ago

请更新至 1.18.2 版本,你需要做的事:

  1. 保持你原来的布局写法
  2. standy 方法的 changeCount 参数需传入相应的更新数:chatScrollObserver.standby(changeCount: 4);