fluttercommunity / backdrop

Backdrop implementation in flutter.
https://pub.dev/packages/backdrop
MIT License
329 stars 37 forks source link

Manually opening Backdrop #131

Closed pishguy closed 1 year ago

pishguy commented 1 year ago

how can i Manually opening Backdrop to show frontLayer without using like sample code action such as:

backLayer: BackdropNavigationBackLayer(
  items: [
    ListTile(title: Text("Widget 1")),
    ListTile(title: Text("Widget 2")),
  ],
  onTap: (int position) => {setState(() => _currentIndex = position)},
),

this is my code and i get Null check operator used on a null value error on this line of code Backdrop.of(context).fling() :

return BackdropScaffold(
  backgroundColor: Colors.white,
  revealBackLayerAtStart: true,
  backLayer: Center(
    child: GestureDetector(
      onTap: () {
        Backdrop.of(context).fling();
      },
      child: Container(
        width: 200,
        height: 100,
        color: Colors.black12,
        child: Text("Back Layer"),
      ),
    ),
  ),
  frontLayer: Center(child: Text("Widget 1")),
);
WieFel commented 1 year ago

Hi @pishguy, thanks for opening the issue. Please check this section of the readme, where we describe that you can't directly access the Backdrop.of(context) within the same widget context, in which you create the BackdropScaffold.

You

  1. either need to put the use of Backdrop.of(context) into a separate widget (i.e. create a new StatelessWidget and put the GestureDetector stuff into it), or
  2. you wrap your GestureDetector (in this case) in a Builder widget. Like that you have a "new" context, from which you can access Backdrop.of(context)
pishguy commented 11 months ago

@WieFel hi again :)

could you help me whats the correct context in this code? i want to close Backdrop manually:

using Builder context:

return ValueListenableBuilder<bool>(
  valueListenable: wm.bottomSheetVisibility,
  builder: (_, bool visibility, __) {
    return Builder(
      builder: (context) {
        return WillPopScope(
          onWillPop: () async {
            if (visibility) {
              wm.onChangeBottomSheetVisibility(context: context, visibility: false);
              return Future.value(false);
            }
            wm.onChangeBottomSheetVisibility(context: context, visibility: true);
            return Future.value(true);
          },
          child: BackdropScaffold(
            revealBackLayerAtStart: true,
            frontLayerActiveFactor: .9,
            resizeToAvoidBottomInset: false,
            onBackLayerConcealed: () {
              wm.bottomSheetVisibility.value = true;
            },
            onBackLayerRevealed: () {
              wm.bottomSheetVisibility.value = false;
            },

onChangeBottomSheetVisibility method:

  void onChangeBottomSheetVisibility({
    required BuildContext context,
    required bool visibility,
  }) {
    bottomSheetVisibility.value = visibility;
    Backdrop.of(context).fling();
  }

using ValueListenableBuilder context:

return ValueListenableBuilder<bool>(
  valueListenable: wm.bottomSheetVisibility,
  builder: (context, bool visibility, __) {
    return WillPopScope(
      onWillPop: () async {
        if (visibility) {
          wm.onChangeBottomSheetVisibility(context: context, visibility: false);
          return Future.value(false);
        }
        wm.onChangeBottomSheetVisibility(context: context, visibility: true);
        return Future.value(true);
      },
      child: BackdropScaffold(

using the widget context:

builder: (_, bool visibility, __) {
  return WillPopScope(
    onWillPop: () async {
      if (visibility) {
        wm.onChangeBottomSheetVisibility(visibility: false);
        return Future.value(false);
      }
      wm.onChangeBottomSheetVisibility(visibility: true);
      return Future.value(true);
    },

onChangeBottomSheetVisibility method:

void onChangeBottomSheetVisibility({required bool visibility}) {
    bottomSheetVisibility.value = visibility;
    Backdrop.of(context).fling();
  }

i'm opening frontLayer by clicking on subHeader widget, thanks so much

WieFel commented 11 months ago

I think the problem here is that the context that you pass to Backdrop.of(context).fling(); in all cases is not the correct one. They are ALL from "above" (outside) the BackdropScaffold. But what you need is a context from ONE LEVEL BELOW the BackdropScaffold.

So, having the ValueListenableBuilder inside the BackdropScaffold (e.g. in the body, if possible) would solve the problem in my opinion. Then, you wouldn't even need an additional Builder, but using the context of the ValueListenableBuilder would be enough.

pishguy commented 11 months ago

@WieFel if i put ValueListenableBuilder inside BackdropScaffold how can i close frontLayer manually with WillPopScope?

my widget has a default context and i tested that before.

is this your meant?

@override
Widget build(ILocationScreenWidgetModel wm) {
  return ValueListenableBuilder<bool>(
    valueListenable: wm.bottomSheetVisibility,
    builder: (context, bool visibility, __) {
      return WillPopScope(
        onWillPop: () async {
          if (visibility) {
            wm.bottomSheetVisibility.value = false;
            Backdrop.of(context).fling();
            return Future.value(false);
          }
          wm.bottomSheetVisibility.value = true;
          return Future.value(true);
        },
        child: BackdropScaffold(
          ...
          onBackLayerConcealed: () {
            wm.bottomSheetVisibility.value = true;
          },
          onBackLayerRevealed: () {
            wm.bottomSheetVisibility.value = false;
          },

problem is closing the frontLayer inside WillPopScope widget not inside of subHeader

return ValueListenableBuilder<bool>(
  valueListenable: wm.bottomSheetVisibility,
  builder: (_, bool visibility, __) {
    return Builder(
      builder: (context) {
        return WillPopScope(
          onWillPop: () async {
            if (visibility) {
              wm.bottomSheetVisibility.value = false;
              Backdrop.of(context).fling();
              return Future.value(false);
            }
            wm.bottomSheetVisibility.value = true;
            return Future.value(true);
          },
          child: BackdropScaffold(
            //...
            subHeader: BackdropSubHeader(
              leading: Builder(
                builder: (context) {
                  return IconButton(
                    onPressed: () => Backdrop.of(context).fling(),
                    icon: const Icon(Icons.close_rounded),
                  );
                },
              ),
WieFel commented 11 months ago

Could you briefly explain again what you are trying to achieve? I am a bit confused... Could you maybe provide a simpler example of what your use case is?

as far as I understand, you have the following:

  1. you have a BackdropScaffold with an initially "open" front layer (?)
  2. you want to close the front layer (conceal the back layer) on some event

is that correct?

pishguy commented 11 months ago

@WieFel

In the default state, the frontLayer widget is hidden, and when I click a button, its state changes to visible. The first fundamental issue was that when I pressed the back button on the phone, I expected to be able to return the frontLayer widget to the hidden state, which was not happening.

The second issue is that when the frontLayer widget is in the hidden state, I should be able to return to the previous screen by clicking the back button.

These two states were not implementable outside of the library, so I modified the library itself to address these issues.

please check this pull request: https://github.com/fluttercommunity/backdrop/pull/135

WieFel commented 11 months ago

I created a small example app for you, which does the following:

  1. There is a home page, which redirects to a page which has a BackdropScaffold
  2. The front layer is "hidden" at start. It can be shown with a button placed on the back layer.
  3. When pressing the back button, or pressing the back arrow in the app bar, it is possible to navigate back to the home page.
import 'package:backdrop/backdrop.dart';
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Backdrop Demo',
      home: Scaffold(
        appBar: AppBar(
          title: const Text("My App"),
        ),
        body: Center(
          // Builder necessary here to be able to use Navigator.push (as we are in the same widget as
          // our MaterialApp)
          child: Builder(builder: (context) {
            return ElevatedButton(
              child: const Text("Open Backdrop Page"),
              onPressed: () => Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => const MyCustomPage(),
                ),
              ),
            );
          }),
        ),
      ),
    );
  }
}

class MyCustomPage extends StatelessWidget {
  const MyCustomPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BackdropScaffold(
      appBar: AppBar(
        title: const Text("Backdrop Example"),
      ),
      concealBacklayerOnBackButton: false,
      revealBackLayerAtStart: true,
      backLayerBackgroundColor: Colors.grey.shade300,
      backLayer: Center(
        // Builder necessary here to be able to call Backdrop.of(context), as we are inside the same
        // widget as our BackdropScaffold
        child: Builder(
          builder: (context) {
            return ElevatedButton(
              onPressed: () => Backdrop.of(context).concealBackLayer(),
              child: const Text("Conceal Back Layer"),
            );
          },
        ),
      ),
      subHeader: const BackdropSubHeader(
        title: Text("Sub Header"),
      ),
      frontLayer: const Center(
        child: Text("Front Layer"),
      ),
    );
  }
}

In my opinion, it should not be necessary to make any change to the library for this. Hope this helps...

pishguy commented 11 months ago

@WieFel i checked your code, thanks

closing frontLayer doesn't work every where such as my project, i tires to use Builder but it couldn't resolve py problem

WieFel commented 11 months ago

Yes, but you need to put it in the correct place. As I said, it needs to be BELOW the BackdropScaffold in the widget tree. Otherwise, Backdrop.of(context) won't find anything...