如何在 flutter 中滚动 SingleChildView 时固定小部件的位置

问题描述 投票:0回答:1

我有一个有状态的小部件类,其中我有一个 SingleChildScrollView,它采用 Column 和一些小部件,假设 w1、w2、w3、w4 和 w5 都是可滚动的,我想要实现的是当用户向上滚动屏幕 w1、w2 ,w4,w5 应该按预期运行,但是 w3 应该在到达固定位置时保持不变,比如(屏幕高度 - 50)。

这是我的代码,我能够获得位置并添加了一个标志“_isStuck”,现在我需要在标志变为真时粘贴 w3 小部件,否则当标志为假时它应该随流程滚动。


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

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  final GlobalKey _key = GlobalKey();
  ScrollController _controller = ScrollController();
  bool _isStuck = false;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
  }

  void _afterLayout(_) {
    _controller.addListener(
      () {
        final RenderBox renderBox =
            _key.currentContext!.findRenderObject() as RenderBox;
        final Offset offset = renderBox.localToGlobal(Offset.zero);
        final double startY = offset.dy;

        if (startY <= 120) {
          setState(() {
            _isStuck = true;
          });
        } else {
          setState(() {
            _isStuck = false;
          });
        }
        print("Check position:  - $startY - $_isStuck");
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      controller: _controller,
      child: Column(
        children: [
          Container(
            height: 400,
            color: Colors.red,
            child: const Text('w1'),
          ),
          Container(
            height: 400,
            color: Colors.green,
            child: const Text('w2'),
          ),
          RepaintBoundary(
            child: Container(
              height: 100,
              color: Colors.blue.shade400,
              key: _key,
              child: const Text('w3'),
            ),
          ),
          Container(
            height: 500,
            color: Colors.yellow,
            child: const Text('w4'),
          ),
          Container(
            height: 500,
            color: Colors.orange,
            child: const Text('w5'),
          ),
        ],
      ),
    );
  }
}

flutter widget scrollview sticky singlechildscrollview
1个回答
0
投票

首先,创建一个

Stack
。在
SingleChildScrollView
中添加一个
Stack
作为第一项。接下来,添加一个
Positioned
小部件,将
w3
作为其子项作为
Stack
中的第二项。这个
Positioned
小部件只有在
_isStuck
为真时才会被渲染。

SingleChildScrollView
小部件内,您也将拥有
w3
小部件,但只有在
_isStuck
为 false 时才可见。

这是代码。

import 'package:flutter/material.dart';

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  final GlobalKey _key = GlobalKey();
  final ScrollController _controller = ScrollController();
  bool _isStuck = false;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
  }

  void _afterLayout(_) {
    _controller.addListener(
      () {
        final RenderBox renderBox =
            _key.currentContext!.findRenderObject() as RenderBox;
        final Offset offset = renderBox.localToGlobal(Offset.zero);
        final double startY = offset.dy;

        setState(() {
          _isStuck = startY <= 120;
        });
        print("Check position:  - $startY - $_isStuck");
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        SingleChildScrollView(
          controller: _controller,
          child: Column(
            children: [
              Container(
                height: 400,
                color: Colors.red,
                child: const Text('w1'),
              ),
              Container(
                height: 400,
                color: Colors.green,
                child: const Text('w2'),
              ),
              if (!_isStuck) _w3(),
              Container(
                height: 500,
                color: Colors.yellow,
                child: const Text('w4'),
              ),
              Container(
                height: 500,
                color: Colors.orange,
                child: const Text('w5'),
              ),
            ],
          ),
        ),
        if (_isStuck)
          Positioned(
            top: MediaQuery.of(context).size.height - 50,
            left: 0,
            right: 0,
            child: _w3(),
          ),
      ],
    );
  }

  Widget _w3() {
    return RepaintBoundary(
      child: Container(
        height: 100,
        color: Colors.blue.shade400,
        child: const Text('w3'),
        key: _key,
      ),
    );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.