Machhindra Neupane
Machhindra Neupane

Reputation: 727

Unable to add horizontal ListView.builder while using overlapping_panels 0.0.3

enter image description here

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("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) {
            // hide something
          } else if (side == RevealSide.left) {
            // show something
          }
        });
      },
    )

Upvotes: 0

Views: 37

Answers (1)

MrOrhan
MrOrhan

Reputation: 1124

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 '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 > 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) {
    // 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<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(),
    ]);
  }
}

Now you can use also the variable 'allowSidePanel' 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.

Upvotes: 1

Related Questions