Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vRedirector.historyBack() does not use reverse transition animation #197

Open
Oubi256 opened this issue Jul 5, 2022 · 8 comments
Open

Comments

@Oubi256
Copy link

Oubi256 commented Jul 5, 2022

In the VRouter body, I defined buildTransition, as well as onPop and onPopSystem.

  Future<void> _onPop(VRedirector vRedirector) async {
    print('_onPop');
    if (vRedirector.historyCanBack()) {
      vRedirector.historyBack();
    }
  }

However, in the case of vRedirector.historyBack(), the animation in the reverse direction is not played. Inside widgets, context.vRouter.historyBack() works as expected, with reverse animation.

onPressed: () {
              if (context.vRouter.historyCanBack()) {
                context.vRouter.historyBack();
              }
            },

Any idea why the reverse animation isn't happening? Or at least a workaround.

Source code
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_svg/svg.dart';
import 'package:mobileway/models/category_model.dart';
import 'package:mobileway/utils/constants.dart';
import 'package:pressable/pressable.dart';
import 'package:vrouter/vrouter.dart';

import '../../bloc/content_bloc/content_bloc.dart';
import '../../models/group_model.dart';

class CustomAppBar extends StatefulWidget implements PreferredSizeWidget {
final int? tabIndex;
final double height;

const CustomAppBar({Key? key, required this.tabIndex, required this.height})
    : super(key: key);

@override
State<CustomAppBar> createState() => _CustomAppBarState();

@override
// TODO: implement preferredSize
Size get preferredSize => Size.fromHeight(height);
}

class _CustomAppBarState extends State<CustomAppBar>
  with SingleTickerProviderStateMixin {
@override
Widget build(BuildContext context) {
  String goBackTitle = 'No name';

  bool isShopListVisible = widget.tabIndex == 1 && context.vRouter.path != "/shops";

  if (isShopListVisible) {
    final regExp = RegExp(r'[0-9]+$');
    String path = context.vRouter.path;
    RegExpMatch? mathId = regExp.firstMatch(path);
    int titleId = int.parse(path.substring(mathId!.start));

    if (path.contains('group')) {
      final List<GroupModel> groups =
          (context.read<ContentBloc>().state as ContentLoadedState).groups;
      GroupModel groupModel = groups.firstWhere((element) => element.id == titleId);
      goBackTitle = groupModel.name;
    } else if (path.contains('category')) {
      final List<CategoryModel> categories =
          (context.read<ContentBloc>().state as ContentLoadedState)
              .categories;
      CategoryModel categoryModel = categories.firstWhere((element) => element.id == titleId);
      goBackTitle = categoryModel.name;
    }
  }


  double sBarHeight = MediaQuery.of(context).viewPadding.top;
  return AppBar(
    backgroundColor: Theme.of(context).scaffoldBackgroundColor,
    shadowColor: Theme.of(context).hoverColor.withOpacity(0.1),
    elevation: 0,
    scrolledUnderElevation: 10,
    flexibleSpace: Column(
      children: [
        Padding(
          padding: EdgeInsets.only(
              bottom: kSmallPadding,
              top: sBarHeight + kSmallPadding,
              left: kDefaultPadding,
              right: kDefaultPadding),
          child: Row(
            mainAxisSize: MainAxisSize.max,
            children: [
              SvgPicture.asset(
                'assets/icons/logo.svg',
                width: 50.r,
                height: 50.r,
              ),
              SizedBox(width: kSmallPadding),
              if (widget.tabIndex == 3) ...[
                Padding(
                  padding: EdgeInsets.symmetric(horizontal: kSmallPadding),
                  child: Text('Mobile Way',
                      style: Theme.of(context)
                          .textTheme
                          .headline5!
                          .copyWith(fontWeight: FontWeight.w600)),
                ),
              ] else ...[
                Expanded(
                    child: Container(
                  height: 50.h,
                  child: CupertinoTextField(
                    suffix: Pressable.opacity(
                      onPressed: () {},
                      child: Padding(
                        padding: EdgeInsets.symmetric(horizontal: 14.h),
                        child: SvgPicture.asset(
                          'assets/icons/search.svg',
                          width: 20.r,
                          height: 20.r,
                          color: Theme.of(context).primaryColor,
                        ),
                      ),
                    ),
                    decoration: BoxDecoration(
                      boxShadow: [
                        BoxShadow(
                          color:
                              Theme.of(context).hoverColor.withOpacity(0.25),
                          spreadRadius: -6,
                          blurRadius: 13,
                          // changes position of shadow
                        )
                      ],
                      borderRadius: BorderRadius.circular(14.r),
                      color: Theme.of(context).scaffoldBackgroundColor,
                    ),
                    textAlignVertical: TextAlignVertical.center,
                    padding: const EdgeInsets.symmetric(horizontal: 14),
                  ),
                ))
              ],
            ],
          ),
        ),
        if (isShopListVisible) ...[
          Pressable.opacity(
            theme: const PressableOpacityTheme(opacityFactor: 0.6),
            onPressed: () {
              if (context.vRouter.historyCanBack()) {
                context.vRouter.historyBack();
              }
            },
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                SizedBox(width: kDefaultPadding),
                SvgPicture.asset(
                  'assets/icons/back-button.svg',
                  width: 12.r,
                  height: 16.r,
                  color: Theme.of(context).primaryColor,
                ),
                SizedBox(width: kSmallPadding),
                Flexible(
                  child: Text(
                    goBackTitle,
                    maxLines: 1,
                    style: Theme.of(context).textTheme.headline2,
                    overflow: TextOverflow.ellipsis,
                  ),
                ),
              ],
            ),
          )
        ],
      ],
    ),
  );
}
}
@lulupointu
Copy link
Owner

The animation in the reverse direction only plays when a page is removed from the top of the stack (which is what happens with pop but which can also be triggered by to if you go to the page bellow the current one. This is actually something which is handled by the Flutter navigator through the TransitionDelegate. This is not something I expose in VRouter but I could if you think this would solve your issue. You could try that by forking VRouter and passing the TransitionDelegate down. Let my know if you need help/have a PR :)

@wizza-smile
Copy link

would this enable arbitrary pop transitions? So the user has the impression of popping the stack while he is actually switching to another nested route?

@lulupointu
Copy link
Owner

TransitionDelegate gives you full control so I guess you could achieve this effect if you wanted to

@wizza-smile
Copy link

How can I access TransitionDelegate through vRouter, please?

@wizza-smile
Copy link

The animation in the reverse direction only plays when a page is removed from the top of the stack (which is what happens with pop but which can also be triggered by to if you go to the page bellow the current one. This is actually something which is handled by the Flutter navigator through the TransitionDelegate. This is not something I expose in VRouter but I could if you think this would solve your issue. You could try that by forking VRouter and passing the TransitionDelegate down. Let my know if you need help/have a PR :)

I did not read well .. 😊 I would like to try a PR on this. Can you give me a push in the right direction? @lulupointu

@lulupointu
Copy link
Owner

Sure awesome!
For a first version you can have a look at how VRouter.observers are implemented. Though VRouter.observers are harder because they need to be unique per navigator so I have to place them into a proxy class.
What you would have to do for transitionDelegateis basically the same but simpler.
The next step would be to check whether it works for your use case. My main concern is that you might want to be able to access something like previousVRouterDataand newVRouterData to be able to dynamically change the transition. To achieve this you have 2 possible design:

  1. Instead of transitionDelegateyou create a transitionDelegateBuilder, a function which gives 1 parameter (which is a class with the previousVRouterDataand newVRouterData, maybe it could be named VTransitionData)
  2. Or you create a VTransitionDelegate class which would be basically the same as TransitionDelegatebut would include previousVRouterDataand newVRouterData in the resolve callback.

Solution 1 might be simpler so I would start with this and see how it feels when using it.

Let me know if you need more help 😊

@wizza-smile
Copy link

I was able to pass a working transitionDelegate into vrouter_delegate.dart.

have a look here:
https://github.com/lulupointu/vrouter/pull/217/files

But I don't understand how to proceed to either case 1. or 2. you mentioned from here.

Where could previousVRouterData and newVRouterData come into play?

Thank you for your support and respect maximum for the library itself :)

@titaniteChuck
Copy link

Hi, I'm just a random user who discovered this package recently.
I've been researching this as well. If you've done the work of exposing the transitionDelegate, maybe just passing this delegate could work: https://github.com/slovnicki/beamer/blob/c3795ab39197cc3429c54c5ec3fa6a1202ddba88/package/lib/src/transition_delegates.dart#L45
?
They capture exiting routes and mark them as pop, which should trigger the reverse animation of VRouter, if I understand correctly.
I do not have the skills to foresee what will happen, and I would struggle forking and testing this myself but I thought I'd share this lead here :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants