|
| 1 | +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file |
| 2 | +// for details. All rights reserved. Use of this source code is governed by a |
| 3 | +// BSD-style license that can be found in the LICENSE file. |
| 4 | + |
| 5 | +// There is no public API exposed yet, the in-progress API lives here. |
| 6 | +import 'package:_fe_analyzer_shared/src/macros/api.dart'; |
| 7 | + |
| 8 | +/// A macro that annotates a class and turns it into an inherited widget. |
| 9 | +/// |
| 10 | +/// This will fill in any "holes" that do not have custom implementations, |
| 11 | +/// specifically the following items will be added if they don't exist: |
| 12 | +/// |
| 13 | +/// - Make the class extend `InheritedWidget`. |
| 14 | +/// - Add a constructor that will initialize any fields that are defined, and |
| 15 | +/// take `key` and `child` parameters which it forwards to the super |
| 16 | +/// constructor. |
| 17 | +/// - Add static `of` and `maybeOf` methods which take a build context and |
| 18 | +/// return an instance of this class using `dependOnIheritedWidgetOfExactType`. |
| 19 | +/// - Add an `updateShouldNotify` method which does checks for equality of all |
| 20 | +/// fields. |
| 21 | +macro class InheritedWidget implements ClassTypesMacro, ClassDeclarationsMacro { |
| 22 | + const InheritedWidget(); |
| 23 | + |
| 24 | + @override |
| 25 | + void buildTypesForClass( |
| 26 | + ClassDeclaration clazz, ClassTypeBuilder builder) { |
| 27 | + if (clazz.superclass != null) return; |
| 28 | + // TODO: Add `extends InheritedWidget` once we have an API for that. |
| 29 | + } |
| 30 | + |
| 31 | + @override |
| 32 | + Future<void> buildDeclarationsForClass(ClassDeclaration clazz, MemberDeclarationBuilder builder) async { |
| 33 | + final fields = await builder.fieldsOf(clazz); |
| 34 | + final methods = await builder.methodsOf(clazz); |
| 35 | + if (!methods.any((method) => method is ConstructorDeclaration)) { |
| 36 | + builder.declareInType(DeclarationCode.fromParts([ |
| 37 | + 'const ${clazz.identifier.name}(', |
| 38 | + '{', |
| 39 | + for (var field in fields)...[ |
| 40 | + field.type.isNullable ? '' : 'required ', |
| 41 | + field.identifier, |
| 42 | + ',', |
| 43 | + ], |
| 44 | + 'super.key,', |
| 45 | + 'required super.child,', |
| 46 | + '});', |
| 47 | + ])); |
| 48 | + } |
| 49 | + |
| 50 | + final buildContext = |
| 51 | + // ignore: deprecated_member_use |
| 52 | + await builder.resolveIdentifier(Uri.parse('package:flutter/widgets.dart'), 'BuildContext'); |
| 53 | + if (!methods.any((method) => method.identifier.name == "maybeOf")) { |
| 54 | + builder.declareInType(DeclarationCode.fromParts([ |
| 55 | + 'static ', |
| 56 | + clazz.identifier, |
| 57 | + '? maybeOf(', |
| 58 | + buildContext, |
| 59 | + ' context) => context.dependOnInheritedWidgetOfExactType<', |
| 60 | + clazz.identifier, |
| 61 | + '>();', |
| 62 | + ])); |
| 63 | + } |
| 64 | + |
| 65 | + if (!methods.any((method) => method.identifier.name == "of")) { |
| 66 | + builder.declareInType(DeclarationCode.fromParts([ |
| 67 | + 'static ', |
| 68 | + clazz.identifier, |
| 69 | + ' of(', |
| 70 | + buildContext, |
| 71 | + ''' context) { |
| 72 | + final result = this.maybeOf(context); |
| 73 | + assert(result != null, 'No ${clazz.identifier.name} found in context'); |
| 74 | + return result!; |
| 75 | + }''', |
| 76 | + ])); |
| 77 | + } |
| 78 | + |
| 79 | + if (!methods.any((method) => method.identifier.name == 'updateShouldNotify')) { |
| 80 | + // ignore: deprecated_member_use |
| 81 | + final override = await builder.resolveIdentifier( |
| 82 | + Uri.parse('package:meta/meta.dart'), 'override'); |
| 83 | + builder.declareInType(DeclarationCode.fromParts([ |
| 84 | + '@', |
| 85 | + override, |
| 86 | + ' bool updateShouldNotify(', |
| 87 | + clazz.identifier, |
| 88 | + ' oldWidget) =>', |
| 89 | + ...[ |
| 90 | + for (var field in fields) |
| 91 | + 'oldWidget.${field.identifier.name} != this.${field.identifier.name}', |
| 92 | + ].joinAsCode(' || '), |
| 93 | + ';', |
| 94 | + ])); |
| 95 | + } |
| 96 | + } |
| 97 | +} |
0 commit comments