Line data Source code
1 : import 'package:beamer/src/utils.dart'; 2 : import 'package:flutter/widgets.dart'; 3 : 4 : import './beam_location.dart'; 5 : import './beam_page.dart'; 6 : 7 : /// Checks whether current [BeamLocation] is allowed to be beamed to 8 : /// and provides steps to be executed following a failed check. 9 : /// 10 : /// If neither [beamTo], [beamToNamed] nor [showPage] is specified, 11 : /// the guard will just block navigation, i.e. nothing will happen. 12 : class BeamGuard { 13 1 : BeamGuard({ 14 : required this.pathBlueprints, 15 : required this.check, 16 : this.onCheckFailed, 17 : this.beamTo, 18 : this.beamToNamed, 19 : this.showPage, 20 : this.guardNonMatching = false, 21 : this.replaceCurrentStack = true, 22 : }); 23 : 24 : /// A list of path strings or regular expressions (using dart's RegExp class) that are to be guarded. 25 : /// 26 : /// For strings: 27 : /// Asterisk wildcard is supported to denote "anything". 28 : /// 29 : /// For example, '/books/*' will match '/books/1', '/books/2/genres', etc. 30 : /// but will not match '/books'. To match '/books' and everything after it, 31 : /// use '/books*'. 32 : /// 33 : /// See [_hasMatch] for more details. 34 : /// 35 : /// For RegExp: 36 : /// You can use RegExp instances and the delegate will check for a match using [RegExp.hasMatch] 37 : /// 38 : /// For example, `RegExp('/books/')` will match '/books/1', '/books/2/genres', etc. 39 : /// but will not match '/books'. To match '/books' and everything after it, 40 : /// use `RegExp('/books')` 41 : List<dynamic> pathBlueprints; 42 : 43 : /// What check should be performed on a given [location], 44 : /// the one to which beaming has been requested. 45 : /// 46 : /// [context] is also injected to fetch data up the tree if necessary. 47 : bool Function(BuildContext context, BeamLocation location) check; 48 : 49 : /// Arbitrary closure to execute when [check] fails. 50 : /// 51 : /// This will run before and regardless of [beamTo], [beamToNamed], [showPage]. 52 : void Function(BuildContext context, BeamLocation location)? onCheckFailed; 53 : 54 : /// If guard [check] returns `false`, build a [BeamLocation] to be beamed to. 55 : /// 56 : /// [showPage] has precedence over this attribute. 57 : BeamLocation Function(BuildContext context)? beamTo; 58 : 59 : /// If guard [check] returns `false`, beam to this URI string. 60 : /// 61 : /// [showPage] has precedence over this attribute. 62 : String? beamToNamed; 63 : 64 : /// If guard [check] returns `false`, put this page onto navigation stack. 65 : /// 66 : /// This has precedence over [beamTo] and [beamToNamed]. 67 : BeamPage? showPage; 68 : 69 : /// Whether to [check] all the path blueprints defined in [pathBlueprints] 70 : /// or [check] all the paths that **are not** in [pathBlueprints]. 71 : /// 72 : /// `false` meaning former and `true` meaning latter. 73 : bool guardNonMatching; 74 : 75 : /// Whether or not to replace the current [BeamLocation]'s stack of pages. 76 : final bool replaceCurrentStack; 77 : 78 : /// Matches [location]'s pathBlueprint to [pathBlueprints]. 79 : /// 80 : /// If asterisk is present, it is enough that the pre-asterisk substring is 81 : /// contained within location's pathBlueprint. 82 : /// Else, the path (i.e. the pre-query substring) of the location's uri 83 : /// must be equal to the pathBlueprint. 84 1 : bool _hasMatch(BeamLocation location) { 85 2 : for (var pathBlueprint in pathBlueprints) { 86 1 : if (pathBlueprint is String) { 87 1 : final asteriskIndex = pathBlueprint.indexOf('*'); 88 2 : if (asteriskIndex != -1) { 89 2 : if (location.state.uri 90 1 : .toString() 91 2 : .contains(pathBlueprint.substring(0, asteriskIndex))) { 92 : return true; 93 : } 94 : } else { 95 4 : if (pathBlueprint == location.state.uri.path) { 96 : return true; 97 : } 98 : } 99 : } else { 100 1 : final regexp = Utils.tryCastToRegExp(pathBlueprint); 101 4 : return regexp.hasMatch(location.state.uri.path); 102 : } 103 : } 104 : return false; 105 : } 106 : 107 : /// Whether or not the guard should [check] the [location]. 108 1 : bool shouldGuard(BeamLocation location) { 109 3 : return guardNonMatching ? !_hasMatch(location) : _hasMatch(location); 110 : } 111 : }