无法在使用 overlapping_panels 0.0.3 时添加水平的 ListView.builder。

huangapple go评论73阅读模式
英文:

Unable to add horizontal ListView.builder while using overlapping_panels 0.0.3

问题

以下是翻译好的部分:

[![在此输入图片描述][1]][1]

我是Flutter开发的新手
水平列表在使用Axis.vertical时可以滚动,但停止滚动。
预期的是只有当列表的所有内容都滚动完毕后才能转到下一个滑块。
问题 = 只显示屏幕上可见的项目列表,无法水平滚动
库链接:https://pub.dev/packages/overlapping_panels

# 代码

```dart
body: OverlappingPanels(
      right: Builder(
          builder: (context)  {
            return Text("right");
          }
      ),
      main: Builder(
        builder: (context) {
          var items = ["item1","Item2", "Item3", "Item4", "Item5", "Item6", "Item7"];
          return Container(
              width: double.infinity,
              height: 200,
              color: Colors.blue,
              child: Scrollbar(
                  child: ListView.builder(
                      scrollDirection: Axis.horizontal,
                      itemCount: items.length,
                      itemBuilder: (BuildContext context , int index) {
                        return Container(
                          width: 150,
                          margin: const EdgeInsets.all(8),
                          child: Center(
                            child: Text(
                              items[index],
                              style: const TextStyle(
                                  color: Colors.black,
                                  fontSize: 18
                              ),
                            ),
                          ),
                        );
                      })
              )
          );

        },
      ),
      onSideChange: (side) {

        setState(() {
          if (side == RevealSide.main) {
            // 隐藏某些内容
          } else if (side == RevealSide.left) {
            // 显示某些内容
          }
        });
      },
    )

<details>
<summary>英文:</summary>

[![enter image description here][1]][1]

I am new to Flutter development
The horizontal list stops scrolling but is able To Scroll while using Axis.vertical.
What is expected is Once All the content of List Scrolls then only go to the next Slider.
Problem = only displays the list of items that we can visible on a screen unable to scroll horizontally  
lib-link https://pub.dev/packages/overlapping_panels

# Code

    body: OverlappingPanels(
          right: Builder(
              builder: (context)  {
                return Text(&quot;right&quot;);
              }
          ),
          main: Builder(
            builder: (context) {
              var items = [&quot;item1&quot;,&quot;Item2&quot;, &quot;Item3&quot;, &quot;Item4&quot;, &quot;Item5&quot;, &quot;Item6&quot;, &quot;Item7&quot;];
              return Container(
                  width: double.infinity,
                  height: 200,
                  color: Colors.blue,
                  child: Scrollbar(
                      child: ListView.builder(
                          scrollDirection: Axis.horizontal,
                          itemCount: items.length,
                          itemBuilder: (BuildContext context , int index) {
                            return Container(
                              width: 150,
                              margin: const EdgeInsets.all(8),
                              child: Center(
                                child: Text(
                                  items[index],
                                  style: const TextStyle(
                                      color: Colors.black,
                                      fontSize: 18
                                  ),
                                ),
                              ),
                            );
                          })
                  )
              );

            },
          ),
          onSideChange: (side) {

            setState(() {
              if (side == RevealSide.main) {
                // hide something
              } else if (side == RevealSide.left) {
                // show something
              }
            });
          },
        )


  [1]: https://i.stack.imgur.com/R8n88.png

</details>


# 答案1
**得分**: 1

我查看了你的代码,也查看了OverlappingPanels库。问题在于,如果你将页面包装在Overlapping Panels中,它会用一个Gesture Detector包装整个屏幕,并监听从右向左滑动的手势。

如果你是新手,我建议尝试其他方法。否则,你可以复制他们的库并创建你自己的类'my_overlapoing_panels.dart',如下所示:

```dart
library overlapping_panels;

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

const double bleedWidth = 20;

/// 显示部分的枚举
enum RevealSide { left, right, main }

/// 用于显示三个视图面板的小部件,[MyOverlappingPanels.main]位于中心,[MyOverlappingPanels.left]和[MyOverlappingPanels.right]也从各自的侧面显示出来。就像你在Discord移动应用程序的导航中看到的那样。
class MyOverlappingPanels extends StatefulWidget {
  /// 左侧面板
  final Widget? left;

  /// 主要面板
  final Widget main;

  /// 右侧面板
  final Widget? right;

  /// 在显示左侧或右侧面板时保持主要面板可见的偏移量。
  final double restWidth;

  final bool allowSidePanel;

  /// 当面板显示完成时通知的回调。
  final ValueChanged<RevealSide>? onSideChange;

  const MyOverlappingPanels({
    this.left,
    required this.main,
    this.right,
    this.restWidth = 40,
    this.onSideChange,
    this.allowSidePanel = true,
    Key? key,
  }) : super(key: key);

  static MyOverlappingPanelsState? of(BuildContext context) {
    return context.findAncestorStateOfType<MyOverlappingPanelsState>();
  }

  @override
  State<StatefulWidget> createState() {
    return MyOverlappingPanelsState();
  }
}

class MyOverlappingPanelsState extends State<MyOverlappingPanels> with TickerProviderStateMixin {
  AnimationController? controller;
  double translate = 0;

  double _calculateGoal(double width, int multiplier) {
    return (multiplier * width) + (-multiplier * widget.restWidth);
  }

  void _onApplyTranslation() {
    final mediaWidth = MediaQuery.of(context).size.width;

    final animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 200));

    animationController.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        if (widget.onSideChange != null) {
          widget.onSideChange!(translate == 0 ? RevealSide.main : (translate > 0 ? RevealSide.left : RevealSide.right));
        }
        animationController.dispose();
      }
    });

    if (translate.abs() >= mediaWidth / 2) {
      final multiplier = (translate > 0 ? 1 : -1);
      final goal = _calculateGoal(mediaWidth, multiplier);
      final Tween<double> tween = Tween(begin: translate, end: goal);

      final animation = tween.animate(animationController);

      animation.addListener(() {
        setState(() {
          translate = animation.value;
        });
      });
    } else {
      final animation = Tween<double>(begin: translate, end: 0).animate(animationController);

      animation.addListener(() {
        setState(() {
          translate = animation.value;
        });
      });
    }

    animationController.forward();
  }

  void reveal(RevealSide direction) {
    // 只能在显示主要面板时显示
    if (translate != 0) {
      return;
    }

    final mediaWidth = MediaQuery.of(context).size.width;

    final multiplier = (direction == RevealSide.left ? 1 : -1);
    final goal = _calculateGoal(mediaWidth, multiplier);

    final animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 200));

    animationController.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        _onApplyTranslation();
        animationController.dispose();
      }
    });

    final animation = Tween<double>(begin: translate, end: goal).animate(animationController);

    animation.addListener(() {
      setState(() {
        translate = animation.value;
      });
    });

    animationController.forward();
  }

  void onTranslate(double delta) {
    setState(() {
      final translate = this.translate + delta;
      if (translate < 0 && widget.right != null || translate > 0 && widget.left != null) {
        this.translate = translate;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Stack(children: [
      Offstage(
        offstage: translate < 0,
        child: widget.left,
      ),
      Offstage(
        offstage: translate > 0,
        child: widget.right,
      ),
      Transform.translate(
        offset: Offset(translate, 0),
        child: widget.main,
      ),
      widget.allowSidePanel
          ? GestureDetector(
              behavior: HitTestBehavior.translucent,
              onHorizontalDragUpdate: (details) {
                onTranslate(details.delta.dx);
              },
              onHorizontalDragEnd: (details) {
                _onApplyTranslation();
              },
            )
          : SizedBox(),
    ]);
  }
}

现在你可以在你的代码中使用变量'allowSidePanel'。如果你将你的代码更新为以下内容:

class TestScreen extends StatefulWidget {
  const TestScreen({super.key});

  @override
  State<TestScreen> createState() => _TestScreenState();
}

class _TestScreenState extends State<TestScreen> {
  ScrollController controller = ScrollController();

  bool allowScroll = false;

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

    // 设置监听器
    controller.addListener(() {
      if (controller.position.atEdge) {
        bool atBegin = controller.position.pixels == 0;
        if (atBegin) {
          /// 在这里你可以稍后允许左侧面板
        } else {
          /// 在这里允许侧面面板
          setState(() {
            allowScroll = true;
          });
        }
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: MyOverlappingPanels(
        allowSidePanel: allowScroll,
        right: Builder(builder: (context) {
          return Text("right");
        }),
        main: Builder(
          builder: (context) {
            var items = ["item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7"];
            return Container(
                width: double.infinity,
                height: 200,
                color: Colors.blue,
                child: ListView.builder(
                    controller: controller,
                    scrollDirection: Axis.horizontal,
                    itemCount: items.length,
                    itemBuilder: (BuildContext context, int index) {
                      return Container(


<details>
<summary>英文:</summary>

I Looked at your code. And also looked at the Library of OverlappingPanels. 
The thing is, if you wrap you page with Overlapping Panels, It wrap you whole Screen with a Gesture Detector and it listens to a gesture to swipe from right to left. 

If you are new, I would try something else. Otherwise you can copy their library and make it to your own class &#39;my_overlapoing_panels.dart like:

library overlapping_panels;

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

const double bleedWidth = 20;

/// Display sections
enum RevealSide { left, right, main }

/// Widget to display three view panels with the [MyOverlappingPanels.main] being
/// in the center, [MyOverlappingPanels.left] and [MyOverlappingPanels.right] also
/// revealing from their respective sides. Just like you will see in the
/// Discord mobile app's navigation.
class MyOverlappingPanels extends StatefulWidget {
/// The left panel
final Widget? left;

/// The main panel
final Widget main;

/// The right panel
final Widget? right;

/// The offset to use to keep the main panel visible when the left or right
/// panel is revealed.
final double restWidth;

final bool allowSidePanel;

/// A callback to notify when a panel reveal has completed.
final ValueChanged<RevealSide>? onSideChange;

const MyOverlappingPanels({
this.left,
required this.main,
this.right,
this.restWidth = 40,
this.onSideChange,
this.allowSidePanel = true,
Key? key,
}) : super(key: key);

static MyOverlappingPanelsState? of(BuildContext context) {
return context.findAncestorStateOfType<MyOverlappingPanelsState>();
}

@override
State<StatefulWidget> createState() {
return MyOverlappingPanelsState();
}
}

class MyOverlappingPanelsState extends State<MyOverlappingPanels> with TickerProviderStateMixin {
AnimationController? controller;
double translate = 0;

double _calculateGoal(double width, int multiplier) {
return (multiplier * width) + (-multiplier * widget.restWidth);
}

void _onApplyTranslation() {
final mediaWidth = MediaQuery.of(context).size.width;

final animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 200));

animationController.addStatusListener((status) {
  if (status == AnimationStatus.completed) {
    if (widget.onSideChange != null) {
      widget.onSideChange!(translate == 0 ? RevealSide.main : (translate &gt; 0 ? RevealSide.left : RevealSide.right));
    }
    animationController.dispose();
  }
});

if (translate.abs() &gt;= mediaWidth / 2) {
  final multiplier = (translate &gt; 0 ? 1 : -1);
  final goal = _calculateGoal(mediaWidth, multiplier);
  final Tween&lt;double&gt; tween = Tween(begin: translate, end: goal);

  final animation = tween.animate(animationController);

  animation.addListener(() {
    setState(() {
      translate = animation.value;
    });
  });
} else {
  final animation = Tween&lt;double&gt;(begin: translate, end: 0).animate(animationController);

  animation.addListener(() {
    setState(() {
      translate = animation.value;
    });
  });
}

animationController.forward();

}

void reveal(RevealSide direction) {
// can only reveal when showing main
if (translate != 0) {
return;
}

final mediaWidth = MediaQuery.of(context).size.width;

final multiplier = (direction == RevealSide.left ? 1 : -1);
final goal = _calculateGoal(mediaWidth, multiplier);

final animationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 200));

animationController.addStatusListener((status) {
  if (status == AnimationStatus.completed) {
    _onApplyTranslation();
    animationController.dispose();
  }
});

final animation = Tween&lt;double&gt;(begin: translate, end: goal).animate(animationController);

animation.addListener(() {
  setState(() {
    translate = animation.value;
  });
});

animationController.forward();

}

void onTranslate(double delta) {
setState(() {
final translate = this.translate + delta;
if (translate < 0 && widget.right != null || translate > 0 && widget.left != null) {
this.translate = translate;
}
});
}

@override
Widget build(BuildContext context) {
return Stack(children: [
Offstage(
offstage: translate < 0,
child: widget.left,
),
Offstage(
offstage: translate > 0,
child: widget.right,
),
Transform.translate(
offset: Offset(translate, 0),
child: widget.main,
),
widget.allowSidePanel
? GestureDetector(
behavior: HitTestBehavior.translucent,
onHorizontalDragUpdate: (details) {
onTranslate(details.delta.dx);
},
onHorizontalDragEnd: (details) {
_onApplyTranslation();
},
)
: SizedBox(),
]);
}
}


Now you can use also the variable  &#39;allowSidePanel&#39; in your code. And If you update your code to:


class TestScreen extends StatefulWidget {
const TestScreen({super.key});

@override
State<TestScreen> createState() => _TestScreenState();
}

class _TestScreenState extends State<TestScreen> {
ScrollController controller = ScrollController();

bool allowScroll = false;

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

// Setup the listener.
controller.addListener(() {
  if (controller.position.atEdge) {
    bool atBegin = controller.position.pixels == 0;
    if (atBegin) {
      /// here you can later allow left panel later
    } else {
      /// here allow sidepannel
      setState(() {
        allowScroll = true;
      });
    }
  }
});

}

@override
Widget build(BuildContext context) {
return Scaffold(
body: MyOverlappingPanels(
allowSidePanel: allowScroll,
right: Builder(builder: (context) {
return Text("right");
}),
main: Builder(
builder: (context) {
var items = ["item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7"];
return Container(
width: double.infinity,
height: 200,
color: Colors.blue,
child: ListView.builder(
controller: controller,
scrollDirection: Axis.horizontal,
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
return Container(
width: 150,
margin: const EdgeInsets.all(8),
child: Container(
padding: EdgeInsets.all(8),
color: Colors.red,
child: Center(
child: Text(
items[index],
style: const TextStyle(color: Colors.black, fontSize: 18),
),
),
),
);
}));
},
),
onSideChange: (side) {
setState(() {
if (side == RevealSide.main) {
/// here deaktivate ssidepannel again
allowScroll = false;
} else if (side == RevealSide.left) {
// show something
}
});
},
),
);
}
}


this will work.

</details>



huangapple
  • 本文由 发表于 2023年6月2日 13:11:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/76387285.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定