├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── lib │ ├── examples │ │ ├── auto_complete.dart │ │ ├── dropdown_menus.dart │ │ ├── flow_menu.dart │ │ └── simple_offset.dart │ └── main.dart ├── pubspec.lock └── pubspec.yaml ├── lib ├── defer_pointer.dart ├── deferred_pointer_handler.dart └── deferred_pointer_handler_link.dart ├── pubspec.lock └── pubspec.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | build/ 32 | 33 | **/windows/ 34 | **/macos/ 35 | **/android/ 36 | **/ios/ 37 | **/web/ 38 | **/linux/ 39 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: ffb2ecea5223acdd139a5039be2f9c796962833d 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.2 2 | * add call to link.descendantNeedsPaint when child is marked as needing a paint 3 | 4 | ## 0.0.1+4 5 | * initial release. 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 gskinner.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | An alternative to Overlay which allows you to easily render and hit test a widget outside its parent bounds. 2 | _Based on the original idea by @shrouxm here: https://github.com/flutter/flutter/issues/75747#issuecomment-907755810_ 3 | 4 | Typically in Flutter, if you offset a widget outside of it's parent bounds hit-testing will break. `DeferPointer` works around this issue by handing off hit-testing and (optionally) rendering to an `DeferredPointerHandler` widget further up the tree. 5 | 6 | While `Overlay` can solve this issue to some degree, using `DeferPointer` offers some benefits: 7 | * just works: no error prone and tedious layer management 8 | * more granular: you can set the bounds to be any ancestor widget you choose 9 | * more flexible: you can choose to paint the child on top, or not 10 | * easier to align/pin to a widget in the tree as that is the default expectation 11 | 12 | This is useful for larger UI components like dropdown menus and sliding panels, as well as just small general styling tweaks. 13 | 14 | ## 🔨 Installation 15 | ```yaml 16 | dependencies: 17 | defer_pointer: ^0.0.2 18 | ``` 19 | 20 | ### ⚙ Import 21 | 22 | ```dart 23 | import 'package:defer_pointer/defer_pointer.dart'; 24 | ``` 25 | 26 | ## 🕹️ Usage 27 | 28 | 1. Wrap a `DeferredPointerHandler` somewhere above the buttons that you wish to hit-test. 29 | 2. Wrap `DeferPointer` around the buttons themselves. 30 | ```dart 31 | Widget build(BuildContext context) { 32 | return DeferredPointerHandler( 33 | child: SizedBox( 34 | width: 100, 35 | height: 100, 36 | child: Stack(clipBehavior: Clip.none, children: [ 37 | // Hang button off the bottom of the content 38 | Positioned( 39 | bottom: -30, 40 | child: DeferPointer(child: _SomeBtn(false)), 41 | ), 42 | // Content 43 | Positioned.fill( 44 | child: Container( 45 | decoration: BoxDecoration(color: Colors.green, boxShadow: [ 46 | BoxShadow(color: Colors.black.withOpacity(1), blurRadius: 4, spreadRadius: 4), 47 | ]), 48 | ), 49 | ), 50 | ])))); 51 | } 52 | ``` 53 | 54 | Enable `paintOnTop` if you need the child Widget painted on top of it's siblings. This will defer painting to the currently linked `DeferredPointerHandler`. 55 | ```dart 56 | return DeferPointer( 57 | paintOnTop: true, 58 | child: TextButton(...)); 59 | ``` 60 | 61 | ### Examples 62 | There are 4 examples in this repo: 63 | 64 | 1. A simple example of offsetting 2 buttons outside their stack: 65 | https://github.com/gskinnerTeam/flutter-defer-pointer/blob/master/example/lib/examples/simple_offset_outside_parent.dart 66 | 67 | 2. A classic desktop/web style dropdown menu: 68 | https://github.com/gskinnerTeam/flutter-defer-pointer/blob/master/example/lib/examples/dropdown_menus.dart 69 | 70 | 3. A animated menu based on the `Flow` widget: 71 | https://github.com/gskinnerTeam/flutter-defer-pointer/blob/master/example/lib/examples/flow_menu.dart 72 | 73 | 4. A auto-complete search field: 74 | https://github.com/gskinnerTeam/flutter-defer-pointer/blob/master/example/lib/examples/auto_complete.dart 75 | 76 | ### Manual Linking 77 | By default a `DeferPointer` widget will look up the closest `DeferredPointerHandler` using it's current context. For more complicated use cases you can manually assign a link to bind a pointer to a handler: 78 | ```dart 79 | final _deferredPointerLink = DeferredPointerHandlerLink(); 80 | ... 81 | Widget build(){ 82 | return DeferredPointerHandler( 83 | link: _deferredPointerLink, 84 | child: Padding( 85 | padding: const EdgeInsets.all(20), 86 | child: DeferPointer( 87 | link: _deferredPointerLink, 88 | child: ..., 89 | )), 90 | ); 91 | } 92 | ``` 93 | ## 🐞 Bugs/Requests 94 | 95 | If you encounter any problems please open an issue. If you feel the library is missing a feature, please raise a ticket on Github and we'll look into it. Pull request are welcome. 96 | 97 | ## 📃 License 98 | 99 | MIT License 100 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: ffb2ecea5223acdd139a5039be2f9c796962833d 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /example/lib/examples/auto_complete.dart: -------------------------------------------------------------------------------- 1 | import 'package:defer_pointer/defer_pointer.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class AutoCompleteExample extends StatelessWidget { 5 | const AutoCompleteExample({Key? key}) : super(key: key); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | body: DeferredPointerHandler( 11 | child: Padding( 12 | padding: const EdgeInsets.all(16), 13 | child: Column( 14 | children: [ 15 | _ExpandingSearchBox(), 16 | const Expanded(child: Center(child: Placeholder())), 17 | ], 18 | ), 19 | ), 20 | ), 21 | ); 22 | } 23 | } 24 | 25 | class _ExpandingSearchBox extends StatelessWidget { 26 | _ExpandingSearchBox({Key? key}) : super(key: key); 27 | final ValueNotifier _textNotifier = ValueNotifier(""); 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return AnimatedBuilder( 32 | animation: _textNotifier, 33 | builder: (_, __) { 34 | double size = (context.findRenderObject() as RenderBox?)?.size.height ?? 1; 35 | String value = _textNotifier.value; 36 | return Stack( 37 | children: [ 38 | // Search Box 39 | TextFormField( 40 | decoration: const InputDecoration(hintText: 'Enter search text'), 41 | onChanged: (value) => _textNotifier.value = value), 42 | // Search Suggestions Dropdown 43 | if (value != "") 44 | Positioned( 45 | top: size - 5, 46 | left: -1, 47 | right: -1, 48 | child: DeferPointer( 49 | paintOnTop: true, 50 | child: _SearchResults(query: value), 51 | ), 52 | ) 53 | ], 54 | ); 55 | }, 56 | ); 57 | } 58 | } 59 | 60 | class _SearchResults extends StatelessWidget { 61 | final String query; 62 | 63 | const _SearchResults({Key? key, required this.query}) : super(key: key); 64 | @override 65 | Widget build(BuildContext context) { 66 | return Container( 67 | width: double.infinity, 68 | color: Colors.grey.shade200, 69 | constraints: const BoxConstraints(maxHeight: 300), 70 | child: SingleChildScrollView( 71 | child: Column( 72 | children: List.generate( 73 | query.length, 74 | (i) => TextButton( 75 | child: const SizedBox(height: 30, width: double.infinity, child: Text("Some Suggestion... ")), 76 | onPressed: () {})), 77 | ), 78 | ), 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /example/lib/examples/dropdown_menus.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:defer_pointer/defer_pointer.dart'; 4 | 5 | class DropdownMenusExample extends StatelessWidget { 6 | const DropdownMenusExample({Key? key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | /// Add a [DeferredPointerHandler] handler at the top of the page 11 | return DeferredPointerHandler( 12 | child: Column( 13 | children: [ 14 | Row(children: const [ 15 | RollOverMenuButton(label: 'Menu1', menu: _PlaceholderMenu()), 16 | RollOverMenuButton(label: 'Menu2', menu: _PlaceholderMenu()), 17 | ]), 18 | const Expanded(child: Placeholder()) 19 | ], 20 | ), 21 | ); 22 | } 23 | } 24 | 25 | class RollOverMenuButton extends StatefulWidget { 26 | const RollOverMenuButton({Key? key, required this.menu, required this.label}) : super(key: key); 27 | final String label; 28 | final Widget menu; 29 | 30 | @override 31 | State createState() => _RollOverMenuButtonState(); 32 | } 33 | 34 | class _RollOverMenuButtonState extends State { 35 | bool _isMouseOverBtn = false; 36 | bool _isMouseOverDropdown = false; 37 | @override 38 | Widget build(BuildContext context) { 39 | // Get the intrinsic height of this widget, positioned items will not count against it. 40 | int height = (context.findRenderObject() as RenderBox?)?.size.height.toInt() ?? 1; 41 | return MouseRegion( 42 | onEnter: (_) => setState(() => _isMouseOverBtn = true), 43 | onExit: (_) => setState(() => _isMouseOverBtn = false), 44 | child: Stack( 45 | children: [ 46 | /// Button 47 | CupertinoButton(child: Text(widget.label), onPressed: () {}), 48 | 49 | /// Dropdown Menu 50 | if (_isMouseOverBtn || _isMouseOverDropdown) 51 | Positioned( 52 | top: height - 2, 53 | // wrap the entire menu in [DeferPointer] all of the menus child buttons will be deferred 54 | child: DeferPointer( 55 | paintOnTop: true, 56 | child: MouseRegion( 57 | onEnter: (_) => setState(() => _isMouseOverDropdown = true), 58 | onExit: (_) => setState(() => _isMouseOverDropdown = false), 59 | child: widget.menu, 60 | ), 61 | ), 62 | ), 63 | ], 64 | ), 65 | ); 66 | } 67 | } 68 | 69 | /// Just a column of buttons, nothing interesting here. 70 | class _PlaceholderMenu extends StatelessWidget { 71 | const _PlaceholderMenu({Key? key}) : super(key: key); 72 | @override 73 | Widget build(BuildContext context) { 74 | return Container( 75 | color: Colors.grey.shade200, 76 | padding: const EdgeInsets.all(16), 77 | child: Column(children: [ 78 | _buildTextButton('Btn 1'), 79 | _buildTextButton('Btn 2'), 80 | _buildTextButton('Btn 3'), 81 | _buildTextButton('Btn 4'), 82 | _buildTextButton('Btn 5'), 83 | _buildTextButton('Btn 6'), 84 | _buildTextButton('Btn 7'), 85 | _buildTextButton('Btn 8'), 86 | ]), 87 | ); 88 | } 89 | 90 | TextButton _buildTextButton(String lbl) { 91 | return TextButton( 92 | onPressed: () {}, 93 | child: ConstrainedBox( 94 | constraints: const BoxConstraints(minWidth: 200, minHeight: 40), 95 | child: Text(lbl), 96 | )); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /example/lib/examples/flow_menu.dart: -------------------------------------------------------------------------------- 1 | import 'package:defer_pointer/defer_pointer.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | /// This is based on the demo here: https://api.flutter.dev/flutter/widgets/Flow-class.html 5 | /// Adds a [DeferredPointerHandler] to the root of the view, and wraps each flowMenuItem in a [DeferPointer]. 6 | /// Also requires that the Flow() widget itself be wrapped in a [DeferPointer] with paintOnTop=true 7 | /// todo(sb): figure out why we need to use 2 [DeferPointer] widgets here 8 | class FlowMenuExample extends StatelessWidget { 9 | const FlowMenuExample({Key? key}) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return MaterialApp( 14 | home: Scaffold( 15 | body: DeferredPointerHandler( 16 | child: Column(children: [ 17 | Row( 18 | children: const [ 19 | FlowMenu(), 20 | Text('I am a title!', style: TextStyle(fontSize: 32)), 21 | ], 22 | ), 23 | const Expanded(child: Placeholder()) 24 | ]), 25 | ), 26 | ), 27 | ); 28 | } 29 | } 30 | 31 | class FlowMenu extends StatefulWidget { 32 | const FlowMenu({Key? key}) : super(key: key); 33 | 34 | @override 35 | State createState() => _FlowMenuState(); 36 | } 37 | 38 | class _FlowMenuState extends State with SingleTickerProviderStateMixin { 39 | late AnimationController menuAnimation = 40 | AnimationController(duration: const Duration(milliseconds: 250), vsync: this); 41 | IconData lastTapped = Icons.notifications; 42 | final List menuItems = [ 43 | Icons.home, 44 | Icons.new_releases, 45 | Icons.notifications, 46 | Icons.settings, 47 | Icons.menu, 48 | ]; 49 | 50 | @override 51 | Widget build(BuildContext context) { 52 | return SizedBox( 53 | width: _btnSize, 54 | height: _btnSize, 55 | child: DeferPointer( 56 | paintOnTop: true, 57 | child: Flow( 58 | clipBehavior: Clip.none, 59 | delegate: FlowMenuDelegate(menuAnimation: menuAnimation), 60 | children: menuItems.map((IconData icon) { 61 | return DeferPointer(child: _buildFlowMenuItem(icon)); 62 | }).toList(), 63 | ), 64 | ), 65 | ); 66 | } 67 | 68 | void _updateMenu(IconData icon) { 69 | if (icon != Icons.menu) { 70 | setState(() => lastTapped = icon); 71 | } 72 | } 73 | 74 | final double _btnSize = 60; 75 | Widget _buildFlowMenuItem(IconData icon) { 76 | return Padding( 77 | padding: const EdgeInsets.symmetric(vertical: 2.0), 78 | child: RawMaterialButton( 79 | fillColor: lastTapped == icon ? Colors.amber[700] : Colors.blue, 80 | splashColor: Colors.amber[100], 81 | shape: const CircleBorder(), 82 | constraints: BoxConstraints.tight(Size(_btnSize, _btnSize)), 83 | onPressed: () { 84 | _updateMenu(icon); 85 | menuAnimation.status == AnimationStatus.completed ? menuAnimation.reverse() : menuAnimation.forward(); 86 | }, 87 | child: Icon(icon, color: Colors.white, size: 12), 88 | ), 89 | ); 90 | } 91 | } 92 | 93 | class FlowMenuDelegate extends FlowDelegate { 94 | FlowMenuDelegate({required this.menuAnimation}) : super(repaint: menuAnimation); 95 | 96 | final Animation menuAnimation; 97 | 98 | @override 99 | bool shouldRepaint(FlowMenuDelegate oldDelegate) { 100 | return menuAnimation != oldDelegate.menuAnimation; 101 | } 102 | 103 | @override 104 | void paintChildren(FlowPaintingContext context) { 105 | double dx = 0.0; 106 | for (int i = 0; i < context.childCount; ++i) { 107 | dx = context.getChildSize(i)!.width * i; 108 | context.paintChild( 109 | i, 110 | transform: Matrix4.translationValues(dx * menuAnimation.value, 0, 0), 111 | ); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /example/lib/examples/simple_offset.dart: -------------------------------------------------------------------------------- 1 | import 'package:defer_pointer/defer_pointer.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class SimpleOffsetExample extends StatelessWidget { 5 | const SimpleOffsetExample({Key? key}) : super(key: key); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Center( 10 | child: DeferredPointerHandler( 11 | child: SizedBox( 12 | width: 100, 13 | height: 100, 14 | child: Stack(clipBehavior: Clip.none, children: [ 15 | Positioned( 16 | bottom: -30, 17 | child: DeferPointer(child: _SomeBtn(false)), 18 | ), 19 | Positioned( 20 | top: -28, 21 | child: DeferPointer(paintOnTop: true, child: _SomeBtn(true)), 22 | ), 23 | // Content 24 | Positioned.fill( 25 | child: Container( 26 | decoration: BoxDecoration(color: Colors.green, boxShadow: [ 27 | BoxShadow(color: Colors.black.withOpacity(1), blurRadius: 4, spreadRadius: 4), 28 | ]), 29 | ), 30 | ), 31 | ])))); 32 | } 33 | 34 | Container _SomeBtn(bool showSpinner) { 35 | return Container( 36 | color: Colors.grey[300], 37 | child: TextButton( 38 | onPressed: () => debugPrint('tap!'), 39 | child: Row( 40 | children: [ 41 | const Text('Click me!'), 42 | if (showSpinner) const SizedBox(width: 20, height: 20, child: CircularProgressIndicator()), 43 | ], 44 | ), 45 | )); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/examples/auto_complete.dart'; 2 | import 'package:example/examples/flow_menu.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'examples/dropdown_menus.dart'; 6 | import 'examples/simple_offset.dart'; 7 | 8 | void main() { 9 | runApp(const MyApp()); 10 | } 11 | 12 | class MyApp extends StatefulWidget { 13 | const MyApp({Key? key}) : super(key: key); 14 | 15 | @override 16 | State createState() => MyAppState(); 17 | } 18 | 19 | class MyAppState extends State with SingleTickerProviderStateMixin { 20 | late TabController tabs = TabController(length: tabBuilders.length, vsync: this); 21 | List tabBuilders = [ 22 | () => const SimpleOffsetExample(), 23 | () => const DropdownMenusExample(), 24 | () => const FlowMenuExample(), 25 | () => const AutoCompleteExample(), 26 | ]; 27 | 28 | @override 29 | void initState() { 30 | tabs.addListener(() => setState(() {})); 31 | super.initState(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | final pages = tabBuilders.map((e) => e.call()).toList(); 37 | return MaterialApp( 38 | home: Scaffold( 39 | body: Column(children: [ 40 | Expanded( 41 | //child: IndexedStack(index: tabs.index, children: pages), 42 | child: TabBarView(controller: tabs, children: pages), 43 | ), 44 | TabBar( 45 | controller: tabs, 46 | tabs: [ 47 | TextButton(onPressed: () => tabs.index = 0, child: const Text('Offset Buttons')), 48 | TextButton(onPressed: () => tabs.index = 1, child: const Text('Dropdown Menu')), 49 | TextButton(onPressed: () => tabs.index = 2, child: const Text('Flow Menu')), 50 | TextButton(onPressed: () => tabs.index = 3, child: const Text('Auto Complete')), 51 | ], 52 | ), 53 | ]), 54 | ), 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.8.1" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.3.1" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.15.0" 46 | cupertino_icons: 47 | dependency: "direct main" 48 | description: 49 | name: cupertino_icons 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.0.4" 53 | defer_pointer: 54 | dependency: "direct main" 55 | description: 56 | path: ".." 57 | relative: true 58 | source: path 59 | version: "0.0.1+5" 60 | equatable: 61 | dependency: transitive 62 | description: 63 | name: equatable 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "2.0.3" 67 | fake_async: 68 | dependency: transitive 69 | description: 70 | name: fake_async 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "1.2.0" 74 | flutter: 75 | dependency: "direct main" 76 | description: flutter 77 | source: sdk 78 | version: "0.0.0" 79 | flutter_lints: 80 | dependency: "direct dev" 81 | description: 82 | name: flutter_lints 83 | url: "https://pub.dartlang.org" 84 | source: hosted 85 | version: "1.0.4" 86 | flutter_test: 87 | dependency: "direct dev" 88 | description: flutter 89 | source: sdk 90 | version: "0.0.0" 91 | lints: 92 | dependency: transitive 93 | description: 94 | name: lints 95 | url: "https://pub.dartlang.org" 96 | source: hosted 97 | version: "1.0.1" 98 | matcher: 99 | dependency: transitive 100 | description: 101 | name: matcher 102 | url: "https://pub.dartlang.org" 103 | source: hosted 104 | version: "0.12.10" 105 | meta: 106 | dependency: transitive 107 | description: 108 | name: meta 109 | url: "https://pub.dartlang.org" 110 | source: hosted 111 | version: "1.7.0" 112 | path: 113 | dependency: transitive 114 | description: 115 | name: path 116 | url: "https://pub.dartlang.org" 117 | source: hosted 118 | version: "1.8.0" 119 | sky_engine: 120 | dependency: transitive 121 | description: flutter 122 | source: sdk 123 | version: "0.0.99" 124 | source_span: 125 | dependency: transitive 126 | description: 127 | name: source_span 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "1.8.1" 131 | stack_trace: 132 | dependency: transitive 133 | description: 134 | name: stack_trace 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "1.10.0" 138 | stream_channel: 139 | dependency: transitive 140 | description: 141 | name: stream_channel 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "2.1.0" 145 | string_scanner: 146 | dependency: transitive 147 | description: 148 | name: string_scanner 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "1.1.0" 152 | term_glyph: 153 | dependency: transitive 154 | description: 155 | name: term_glyph 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "1.2.0" 159 | test_api: 160 | dependency: transitive 161 | description: 162 | name: test_api 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "0.4.2" 166 | typed_data: 167 | dependency: transitive 168 | description: 169 | name: typed_data 170 | url: "https://pub.dartlang.org" 171 | source: hosted 172 | version: "1.3.0" 173 | vector_math: 174 | dependency: transitive 175 | description: 176 | name: vector_math 177 | url: "https://pub.dartlang.org" 178 | source: hosted 179 | version: "2.1.0" 180 | sdks: 181 | dart: ">=2.12.0 <3.0.0" 182 | flutter: ">=1.17.0" 183 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ">=2.12.0 <3.0.0" 22 | 23 | # Dependencies specify other packages that your package needs in order to work. 24 | # To automatically upgrade your package dependencies to the latest versions 25 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 26 | # dependencies can be manually updated by changing the version numbers below to 27 | # the latest version available on pub.dev. To see which dependencies have newer 28 | # versions available, run `flutter pub outdated`. 29 | dependencies: 30 | flutter: 31 | sdk: flutter 32 | defer_pointer: 33 | path: ../ 34 | 35 | # The following adds the Cupertino Icons font to your application. 36 | # Use with the CupertinoIcons class for iOS style icons. 37 | cupertino_icons: ^1.0.2 38 | 39 | dev_dependencies: 40 | flutter_test: 41 | sdk: flutter 42 | 43 | # The "flutter_lints" package below contains a set of recommended lints to 44 | # encourage good coding practices. The lint set provided by the package is 45 | # activated in the `analysis_options.yaml` file located at the root of your 46 | # package. See that file for information about deactivating specific lint 47 | # rules and activating additional ones. 48 | flutter_lints: ^1.0.0 49 | 50 | # For information on the generic Dart part of this file, see the 51 | # following page: https://dart.dev/tools/pub/pubspec 52 | 53 | # The following section is specific to Flutter. 54 | flutter: 55 | 56 | # The following line ensures that the Material Icons font is 57 | # included with your application, so that you can use the icons in 58 | # the material Icons class. 59 | uses-material-design: true 60 | 61 | # To add assets to your application, add an assets section, like this: 62 | # assets: 63 | # - images/a_dot_burr.jpeg 64 | # - images/a_dot_ham.jpeg 65 | 66 | # An image asset can refer to one or more resolution-specific "variants", see 67 | # https://flutter.dev/assets-and-images/#resolution-aware. 68 | 69 | # For details regarding adding assets from package dependencies, see 70 | # https://flutter.dev/assets-and-images/#from-packages 71 | 72 | # To add custom fonts to your application, add a fonts section here, 73 | # in this "flutter" section. Each entry in this list should have a 74 | # "family" key with the font family name, and a "fonts" key with a 75 | # list giving the asset and other descriptors for the font. For 76 | # example: 77 | # fonts: 78 | # - family: Schyler 79 | # fonts: 80 | # - asset: fonts/Schyler-Regular.ttf 81 | # - asset: fonts/Schyler-Italic.ttf 82 | # style: italic 83 | # - family: Trajan Pro 84 | # fonts: 85 | # - asset: fonts/TrajanPro.ttf 86 | # - asset: fonts/TrajanPro_Bold.ttf 87 | # weight: 700 88 | # 89 | # For details regarding fonts from package dependencies, 90 | # see https://flutter.dev/custom-fonts/#from-packages 91 | -------------------------------------------------------------------------------- /lib/defer_pointer.dart: -------------------------------------------------------------------------------- 1 | library expanded_hit_test; 2 | 3 | import 'dart:collection'; 4 | 5 | import 'package:equatable/equatable.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter/rendering.dart'; 8 | 9 | part 'deferred_pointer_handler_link.dart'; 10 | part 'deferred_pointer_handler.dart'; 11 | 12 | /// Create a StatelessWidget to wrap our RenderObjectWidget so we can bind to inherited widget. 13 | class DeferPointer extends StatelessWidget { 14 | const DeferPointer({Key? key, required this.child, this.paintOnTop = false, this.link}) : super(key: key); 15 | final Widget child; 16 | 17 | /// child will be painted in the [DeferredPointerHandler] causing it to render on top of any siblings in the it's current context. 18 | final bool paintOnTop; 19 | 20 | /// an optional link that can be shared with a [DeferredPointerHandler], 21 | /// if not provided, `DeferredHitTestRegion.of()` will be called. 22 | final DeferredPointerHandlerLink? link; 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | final link = this.link ?? DeferredPointerHandler.of(context).link; 27 | return _DeferPointerRenderObjectWidget(link: link, child: child, deferPaint: paintOnTop); 28 | } 29 | } 30 | 31 | /// Single child render object returns a custom render object [_DeferPointerRenderObject] 32 | class _DeferPointerRenderObjectWidget extends SingleChildRenderObjectWidget { 33 | const _DeferPointerRenderObjectWidget({required this.link, required Widget child, Key? key, required this.deferPaint}) 34 | : super(child: child, key: key); 35 | 36 | final DeferredPointerHandlerLink link; 37 | 38 | final bool deferPaint; 39 | 40 | @override 41 | RenderObject createRenderObject(BuildContext context) => _DeferPointerRenderObject(link, deferPaint); 42 | 43 | @override 44 | void updateRenderObject(BuildContext context, _DeferPointerRenderObject renderObject) { 45 | renderObject.link = link; 46 | renderObject.deferPaint = deferPaint; 47 | } 48 | } 49 | 50 | class _DeferPointerRenderObject extends RenderProxyBox { 51 | _DeferPointerRenderObject(DeferredPointerHandlerLink link, this.deferPaint, {RenderBox? child}) : super(child) { 52 | this.link = link; 53 | } 54 | 55 | bool deferPaint; 56 | bool _linked = false; 57 | 58 | late DeferredPointerHandlerLink _link; 59 | DeferredPointerHandlerLink get link => _link; 60 | set link(DeferredPointerHandlerLink link) { 61 | _link = link; 62 | link.add(this); 63 | _linked = true; 64 | } 65 | 66 | @override 67 | set child(RenderBox? child) { 68 | if (_linked) { 69 | link.remove(this); 70 | _linked = false; 71 | } 72 | super.child = child; 73 | if (this.child != null) { 74 | link.add(this); 75 | _linked = true; 76 | } 77 | } 78 | 79 | @override 80 | void attach(covariant PipelineOwner owner) { 81 | super.attach(owner); 82 | link.add(this); 83 | } 84 | 85 | @override 86 | void detach() { 87 | link.remove(this); 88 | super.detach(); 89 | } 90 | 91 | @override 92 | bool hitTest(BoxHitTestResult result, {required Offset position}) => false; 93 | 94 | @override 95 | void paint(PaintingContext context, Offset offset) { 96 | if (deferPaint) return; // skip the draw if an ancestor is supposed to handle it 97 | context.paintChild(child!, offset); 98 | } 99 | 100 | @override 101 | void markNeedsPaint() { 102 | if (deferPaint) { 103 | _link.descendantNeedsPaint(); 104 | } else { 105 | super.markNeedsPaint(); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /lib/deferred_pointer_handler.dart: -------------------------------------------------------------------------------- 1 | part of 'defer_pointer.dart'; 2 | 3 | /// Handles paint and hit testing for descendant [DeferPointer] widgets. 4 | /// Deferred painting (aka 'paint on top') is optional and can be defined per [DeferPointer]. 5 | class DeferredPointerHandler extends StatefulWidget { 6 | const DeferredPointerHandler({Key? key, required this.child, this.link}) : super(key: key); 7 | final Widget child; 8 | final DeferredPointerHandlerLink? link; 9 | @override 10 | DeferredPointerHandlerState createState() => DeferredPointerHandlerState(); 11 | 12 | /// The state from the closest instance of this class that encloses the given context. 13 | static DeferredPointerHandlerState of(BuildContext context) { 14 | final inherited = context.dependOnInheritedWidgetOfExactType<_InheritedDeferredPaintSurface>(); 15 | assert(inherited != null, 'DeferredPaintSurface was not found on this context.'); 16 | return inherited!.state; 17 | } 18 | } 19 | 20 | /// Holds an internal [DeferredPointerHandlerLink] which can be found using 21 | /// [DeferredPointerHandler].of(context).link. 22 | /// Also accepts an external link which will be used instead of the internal one. 23 | class DeferredPointerHandlerState extends State { 24 | final DeferredPointerHandlerLink _link = DeferredPointerHandlerLink(); 25 | get link => _link; 26 | 27 | @override 28 | void didUpdateWidget(covariant DeferredPointerHandler oldWidget) { 29 | if (widget.link != null) { 30 | _link.removeAll(); 31 | } 32 | super.didUpdateWidget(oldWidget); 33 | } 34 | 35 | @override 36 | void dispose() 37 | { 38 | _link.dispose(); 39 | super.dispose(); 40 | } 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | return _InheritedDeferredPaintSurface( 45 | state: this, 46 | child: _DeferredHitTargetRenderObjectWidget(link: widget.link ?? _link, child: widget.child), 47 | ); 48 | } 49 | } 50 | 51 | //////////////////////////////// 52 | // RENDER OBJECT WIDGET 53 | 54 | class _DeferredHitTargetRenderObjectWidget extends SingleChildRenderObjectWidget { 55 | const _DeferredHitTargetRenderObjectWidget({required this.link, Widget? child, Key? key}) 56 | : super(child: child, key: key); 57 | 58 | final DeferredPointerHandlerLink link; 59 | 60 | @override 61 | RenderObject createRenderObject(BuildContext context) => _DeferredHitTargetRenderObject(link); 62 | 63 | @override 64 | void updateRenderObject(BuildContext context, _DeferredHitTargetRenderObject renderObject) => 65 | renderObject.link = link; 66 | } 67 | 68 | //////////////////////////////// 69 | // RENDER OBJECT PAINTER 70 | 71 | class _DeferredHitTargetRenderObject extends RenderProxyBox { 72 | _DeferredHitTargetRenderObject(DeferredPointerHandlerLink link, [RenderBox? child]) : super(child) { 73 | this.link = link; 74 | } 75 | 76 | DeferredPointerHandlerLink? _link; 77 | DeferredPointerHandlerLink get link => _link!; 78 | set link(DeferredPointerHandlerLink link) { 79 | if (_link != null) { 80 | _link!.removeListener(markNeedsPaint); 81 | } 82 | _link = link; 83 | this.link.addListener(markNeedsPaint); 84 | markNeedsPaint(); 85 | } 86 | 87 | @override 88 | bool hitTest(BoxHitTestResult result, {required Offset position}) { 89 | for (final painter in link.painters.reversed) { 90 | final hit = result.addWithPaintTransform( 91 | transform: painter.child!.getTransformTo(this), 92 | position: position, 93 | hitTest: (BoxHitTestResult result, Offset? position) { 94 | return painter.child!.hitTest(result, position: position!); 95 | }, 96 | ); 97 | if (hit) { 98 | return true; 99 | } 100 | } 101 | return child?.hitTest(result, position: position) ?? false; 102 | } 103 | 104 | @override 105 | // paint all the children that want to be rendered on top 106 | void paint(PaintingContext context, Offset offset) { 107 | super.paint(context, offset); 108 | for (final painter in link.painters) { 109 | if (painter.deferPaint == false) continue; 110 | context.paintChild( 111 | painter.child!, 112 | painter.child!.localToGlobal(Offset.zero, ancestor: this) + offset, 113 | ); 114 | } 115 | } 116 | } 117 | 118 | //////////////////////////////// 119 | // INHERITED WIDGET 120 | 121 | class _InheritedDeferredPaintSurface extends InheritedWidget { 122 | const _InheritedDeferredPaintSurface({Key? key, required Widget child, required this.state}) 123 | : super(key: key, child: child); 124 | 125 | final DeferredPointerHandlerState state; 126 | @override 127 | bool updateShouldNotify(covariant InheritedWidget oldWidget) => false; 128 | } 129 | -------------------------------------------------------------------------------- /lib/deferred_pointer_handler_link.dart: -------------------------------------------------------------------------------- 1 | part of 'defer_pointer.dart'; 2 | 3 | /// Holds a list of [_DeferPointerRenderObject]s which the [DeferredPointerHandler] widget uses to perform hit tests. 4 | class DeferredPointerHandlerLink extends ChangeNotifier with EquatableMixin { 5 | DeferredPointerHandlerLink(); 6 | final List<_DeferPointerRenderObject> _painters = []; 7 | 8 | void descendantNeedsPaint() => notifyListeners(); 9 | 10 | /// All painters currently attached to this link 11 | List<_DeferPointerRenderObject> get painters => UnmodifiableListView(_painters); 12 | 13 | /// Add a render object to the link. Does nothing if item already exists. 14 | void add(_DeferPointerRenderObject value) { 15 | if (!_painters.contains(value)) { 16 | _painters.add(value); 17 | notifyListeners(); 18 | } 19 | } 20 | 21 | /// Remove a render object from the link. Does nothing if item is not in list. 22 | void remove(_DeferPointerRenderObject value) { 23 | if (_painters.contains(value)) { 24 | _painters.remove(value); 25 | notifyListeners(); 26 | } 27 | } 28 | 29 | /// Clears all currently attached links 30 | void removeAll() { 31 | _painters.clear(); 32 | notifyListeners(); 33 | } 34 | 35 | @override 36 | List get props => _painters; 37 | } 38 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.8.1" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.3.1" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.15.0" 46 | equatable: 47 | dependency: "direct main" 48 | description: 49 | name: equatable 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "2.0.3" 53 | fake_async: 54 | dependency: transitive 55 | description: 56 | name: fake_async 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.2.0" 60 | flutter: 61 | dependency: "direct main" 62 | description: flutter 63 | source: sdk 64 | version: "0.0.0" 65 | flutter_lints: 66 | dependency: "direct dev" 67 | description: 68 | name: flutter_lints 69 | url: "https://pub.dartlang.org" 70 | source: hosted 71 | version: "1.0.4" 72 | flutter_test: 73 | dependency: "direct dev" 74 | description: flutter 75 | source: sdk 76 | version: "0.0.0" 77 | lints: 78 | dependency: transitive 79 | description: 80 | name: lints 81 | url: "https://pub.dartlang.org" 82 | source: hosted 83 | version: "1.0.1" 84 | matcher: 85 | dependency: transitive 86 | description: 87 | name: matcher 88 | url: "https://pub.dartlang.org" 89 | source: hosted 90 | version: "0.12.10" 91 | meta: 92 | dependency: transitive 93 | description: 94 | name: meta 95 | url: "https://pub.dartlang.org" 96 | source: hosted 97 | version: "1.7.0" 98 | path: 99 | dependency: transitive 100 | description: 101 | name: path 102 | url: "https://pub.dartlang.org" 103 | source: hosted 104 | version: "1.8.0" 105 | sky_engine: 106 | dependency: transitive 107 | description: flutter 108 | source: sdk 109 | version: "0.0.99" 110 | source_span: 111 | dependency: transitive 112 | description: 113 | name: source_span 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "1.8.1" 117 | stack_trace: 118 | dependency: transitive 119 | description: 120 | name: stack_trace 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "1.10.0" 124 | stream_channel: 125 | dependency: transitive 126 | description: 127 | name: stream_channel 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "2.1.0" 131 | string_scanner: 132 | dependency: transitive 133 | description: 134 | name: string_scanner 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "1.1.0" 138 | term_glyph: 139 | dependency: transitive 140 | description: 141 | name: term_glyph 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "1.2.0" 145 | test_api: 146 | dependency: transitive 147 | description: 148 | name: test_api 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "0.4.2" 152 | typed_data: 153 | dependency: transitive 154 | description: 155 | name: typed_data 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "1.3.0" 159 | vector_math: 160 | dependency: transitive 161 | description: 162 | name: vector_math 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "2.1.0" 166 | sdks: 167 | dart: ">=2.12.0 <3.0.0" 168 | flutter: ">=1.17.0" 169 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: defer_pointer 2 | description: An alternative to Overlay which allows you to easily render and hit test a widget outside its parent bounds. 3 | version: 0.0.2 4 | homepage: https://github.com/gskinnerTeam/flutter-defer-pointer/ 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | flutter: ">=1.17.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | equatable: ^2.0.3 14 | 15 | dev_dependencies: 16 | flutter_test: 17 | sdk: flutter 18 | flutter_lints: ^1.0.0 19 | 20 | # For information on the generic Dart part of this file, see the 21 | # following page: https://dart.dev/tools/pub/pubspec 22 | 23 | # The following section is specific to Flutter. 24 | flutter: 25 | 26 | # To add assets to your package, add an assets section, like this: 27 | # assets: 28 | # - images/a_dot_burr.jpeg 29 | # - images/a_dot_ham.jpeg 30 | # 31 | # For details regarding assets in packages, see 32 | # https://flutter.dev/assets-and-images/#from-packages 33 | # 34 | # An image asset can refer to one or more resolution-specific "variants", see 35 | # https://flutter.dev/assets-and-images/#resolution-aware. 36 | 37 | # To add custom fonts to your package, add a fonts section here, 38 | # in this "flutter" section. Each entry in this list should have a 39 | # "family" key with the font family name, and a "fonts" key with a 40 | # list giving the asset and other descriptors for the font. For 41 | # example: 42 | # fonts: 43 | # - family: Schyler 44 | # fonts: 45 | # - asset: fonts/Schyler-Regular.ttf 46 | # - asset: fonts/Schyler-Italic.ttf 47 | # style: italic 48 | # - family: Trajan Pro 49 | # fonts: 50 | # - asset: fonts/TrajanPro.ttf 51 | # - asset: fonts/TrajanPro_Bold.ttf 52 | # weight: 700 53 | # 54 | # For details regarding fonts in packages, see 55 | # https://flutter.dev/custom-fonts/#from-packages 56 | --------------------------------------------------------------------------------