Skip to content

消息内部有折叠、展开的时候消息位置固定,当展开的内容很长的时候 standby 位置固定无效 #112

@wksmile

Description

@wksmile

大佬你好,我按照
Originally posted by @wksmile in #103 的方法确实能实现展开位置固定,但是有一个问题,就是当展开的内容很长超过一屏的时候展开收起位置就不能固定了。

如图,展开很长位置固定失效。

Image

写个一个 demo 问题复现代码如下:
scroll_list_page.dart 文件

import './expand_item.dart';

class MessageItem {
  final String content;
  final List<String> children;

  const MessageItem({required this.content, required this.children});
}

@RoutePage()
class ScrollListPage extends StatefulWidget {
  const ScrollListPage({super.key});

  @override
  State<ScrollListPage> createState() => _scrollListPage();
}

class _scrollListPage extends State<ScrollListPage> with WidgetsBindingObserver {
  final ScrollController scrollController = ScrollController();
  late ListObserverController observerController;
  late ChatScrollObserver chatObserver;

  List<MessageItem> messages = [
    MessageItem(
        content:
            '''监测**   - **尽可能上传最近的数据**,例如体温、心率血压、呼吸频率等,这有助于更精确地分析头痛原因。   - 记录头痛的发生时间、频率和持续时间,以及是否伴随其他症状。#### **4. 改善睡眠**   - 保持规律睡眠时间,晚上尽量避免在屏幕前时间过长。   - 可在睡前喝些温牛奶,或通过泡脚及放松呼吸训练,帮助身体进入更深的睡眠状态。---### **需要特别关注的情况**如果头痛伴有以下症状,请及时反馈或联系医生:- 持续加剧的头痛,并伴有明显的视力模糊、呕吐或晕厥。- 任何异常变化,比如剧烈恶心、无法言语或肢体无力。请根据建议逐步调节,如果头痛持续存在或频繁加重,及时向我反馈最新的症状与监测数据!''',
        children: [
          '''onize the data.- restingHeartRates:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:25:00: 30.0 BPMYou can kindly remind the user to synchronize the data.Reference: Apple Health[Retrieve respiratory health metrics to assess any breathing-related issues linked to headaches.]:- respiratoryRates:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:20:00: 12.0 BPMYou can kindly remind the user to synchronize the data.- oxygenSaturations:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:24:00: 1.0 %You can kindly remind the user to synchronize the data.Reference: Apple Health[Query lab results for indicators related to metabolic or systemic conditions that might cause headaches.]:No related information was found. Please verify if the relevant data has been uploaded or synchronized''',
        ]),
    MessageItem(
        content:
            '''这条消息第一个展开内容很多长展开有问题,位置不能固定''',
        children: [
          '''Identify the cause of the user's headache and provide possible solutions or recommendations.</summary>[Query vital signs data to check for any abnormalities that could be associated with headaches.]:- bodyTemperatures:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:23:00: 37.78 °CYou can kindly remind the user to synchronize the data.- restingHeartRates:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:25:00: 30.0 BPMYou can kindly remind the user to synchronize the data.- bloodGlucoses:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:23:00: 100.0 mg/dLYou can kindly remind the user to synchronize the data.- systolicPressures:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:24:00: 100.0 mmHgYou can kindly remind the user to synchronize the data.- diastolicPressures:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:24:00: 60.0 mmHgYou can kindly remind the user to synchronize the data.- oxygenSaturations:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:24:00: 1.0 %You can kindly remind the user to synchronize the data.- hrvDatas:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:25:00: 30.0 msYou can kindly remind the user to synchronize the data.- heartRates:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 20:22:00: 101.0 BPMYou can kindly remind the user to synchronize the data.- respiratoryRates:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:20:00: 12.0 BPMYou can kindly remind the user to synchronize the data.Reference: Apple Health[Retrieve heart-related metrics to identify any cardiovascular factors contributing to headaches.]:- hrvDatas:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:25:00: 30.0 msYou can kindly remind the user to synchronize the data.- heartRates:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 20:22:00: 101.0 BPMYou can kindly remind the user to synchronize the data.- restingHeartRates:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:25:00: 30.0 BPMYou can kindly remind the user to synchronize the data.Reference: Apple Health[Retrieve respiratory health metrics to assess any breathing-related issues linked to headaches.]:- respiratoryRates:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:20:00: 12.0 BPMYou can kindly remind the user to synchronize the data.- oxygenSaturations:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:24:00: 1.0 %You can kindly remind the user to synchronize the data.Reference: Apple Health[Query lab results for indicators related to metabolic or systemic conditions that might cause headaches.]:No related information was found. Please verify if the relevant data has been uploaded or synchronized''',
          '''respiratory health metrics to assess any breathing-related issues linked to headaches.]:- respiratoryRates:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:20:00: 12.0 BPMYou can kindly remind the user to synchronize the data.- oxygenSaturations:Data is not available for 2025-01-06 - 2025-01-13. Here is the last valid data on 2024-11-18 17:24:00: 1.0 %You can kindly remind the user to synchronize the data.Reference: Apple Health[Query lab results for indicators related to metabolic or systemic conditions that might cause headaches.]:No related information was found. Please verify if the relevant data has been uploaded or synchronized''',
          '''data.Reference: Apple Health[Query lab results for indicators related to metabolic or systemic conditions that might cause headaches.]:No related information was found. Please verify if the relevant data has been uploaded or synchronized''',
        ]),
    MessageItem(content: '''步数数据!''', children: [
      '''A relevant data has been uploaded or synchronized''',
    ]),
    MessageItem(content: '''步数数据!''', children: [
      '''A relevant data has been uploaded or synchronized''',
    ]),
    MessageItem(content: '''步数数据!''', children: [
      '''A relevant data has been uploaded or synchronized''',
    ]),
    MessageItem(content: '''步数数据!''', children: [
      '''A relevant data has been uploaded or synchronized''',
    ]),
    MessageItem(content: '''步数数据!''', children: [
      '''A relevant data has been uploaded or synchronized''',
    ]),
    MessageItem(content: '''步数数据!''', children: [
      '''A relevant data has been uploaded or synchronized''',
    ]),
    MessageItem(content: '''步数数据!''', children: [
      '''A relevant data has been uploaded or synchronized''',
    ]),
    MessageItem(content: '''步数数据!''', children: [
      '''A relevant data has been uploaded or synchronized''',
    ]),
    MessageItem(content: '''步数数据!''', children: [
      '''A relevant data has been uploaded or synchronized''',
    ]),
    MessageItem(content: '''步数数据!''', children: [
      '''A relevant data has been uploaded or synchronized''',
    ]),
    MessageItem(content: '''步数数据!''', children: [
      '''A relevant data has been uploaded or synchronized''',
    ]),
    MessageItem(content: '''步数数据!''', children: [
      '''A relevant data has been uploaded or synchronized''',
    ]),
  ];

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

  @override
  void dispose() {
    observerController.controller?.dispose();
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

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

    chatObserver.observeSwitchShrinkWrap();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: Colors.white,
        resizeToAvoidBottomInset: true,
        body: Align(
          alignment: Alignment.topCenter,
          child: ListViewObserver(
            controller: observerController,
            child: ListView.builder(
              physics: ChatObserverClampingScrollPhysics(observer: chatObserver),
              itemCount: messages.length,
              reverse: true,
              controller: scrollController, // 设置 ScrollController
              shrinkWrap: chatObserver.isShrinkWrap,
              padding: const EdgeInsets.only(left: 16, right: 16, top: 20, bottom: 10),
              itemBuilder: (context, index) {
                return _itemBuilder(
                  context,
                  index,
                );
              },
            ),
          ),
        ));
  }

  Widget _itemBuilder(BuildContext context, int index) {
    MessageItem item = messages[index];
    return Container(
        margin: EdgeInsets.only(bottom: 10),
        width: 150,
        color: Colors.grey[200],
        child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
          ...item.children.map((e) => ExpandBox(childContent: e, callback: expandCb, index: index)),
          Text(item.content),
        ]));
  }

  void initChatObserver() {
    observerController = ListObserverController(
      controller: scrollController,
    )..cacheJumpIndexOffset = false;

    chatObserver = ChatScrollObserver(observerController)
      ..fixedPositionOffset = 5
      ..toRebuildScrollViewCallback = () {
        setState(() {});
      };
  }

  void expandCb(int index) {
    // 对比上一个 item 来定位
    final refItemIndex = index + 1;
    chatObserver.standby(
      mode: ChatScrollObserverHandleMode.specified,
      refIndexType: ChatScrollObserverRefIndexType.itemIndex,
      refItemIndex: refItemIndex,
      refItemIndexAfterUpdate: refItemIndex,
    );
  }
}

expand_item.dart 文件


class ExpandBox extends StatefulWidget {
  final String childContent;
  final Function? callback;
  final int index;

  const ExpandBox({
    super.key,
    required this.childContent,
    this.callback,
    required this.index,
  });

  @override
  State<ExpandBox> createState() => _ExpandBoxState();
}

class _ExpandBoxState extends State<ExpandBox> {
  bool isExpanded = false;

  @override
  Widget build(
    BuildContext context,
  ) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        ElevatedButton(
            onPressed: () {
              setState(() {
                isExpanded = !isExpanded;
              });
              widget.callback!(widget.index);
            },
            child: isExpanded ? Text('收起') : Text('展开')),
        if (isExpanded) Text(widget.childContent),
        if (isExpanded) Text('Expanded content'),
      ],
    );
  }
}

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions