-
Notifications
You must be signed in to change notification settings - Fork 149
Create Flutter adapter for GenUiValueNotifier. #860
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
base: main
Are you sure you want to change the base?
Changes from all commits
bea9b6d
3000d87
426ddec
0daa1a8
6540f3d
7f0ca32
4f1f6ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,50 @@ | ||||||||||||||||||||||||
| // Copyright 2025 The Flutter Authors. | ||||||||||||||||||||||||
| // Use of this source code is governed by a BSD-style license that can be | ||||||||||||||||||||||||
polina-c marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| // found in the LICENSE file. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import 'package:a2ui_core/a2ui_core.dart'; | ||||||||||||||||||||||||
| import 'package:flutter/foundation.dart'; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// Extension to convert [GenUiListenable] to [Listenable]. | ||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||
| /// Enables using [GenUiListenable] with Flutter widgets | ||||||||||||||||||||||||
| /// that accept [Listenable]. | ||||||||||||||||||||||||
| extension FlutterListenable on GenUiListenable { | ||||||||||||||||||||||||
|
Comment on lines
+8
to
+12
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Creating a new
Suggested change
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. flutter build creates a lot of classes on every call. if class is lightweight (like this adapter) it is ok, it will be easily garbage collected. |
||||||||||||||||||||||||
| Listenable listenable() { | ||||||||||||||||||||||||
| return FlutterListenableAdapter(this); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| /// Extensions to convert GenUi value listenables to Flutter value listenables. | ||||||||||||||||||||||||
| extension GenUiValueListenableFlutterExtension<T> on GenUiValueListenable<T> { | ||||||||||||||||||||||||
polina-c marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| /// Converts this [GenUiValueListenable] to a Flutter [ValueListenable]. | ||||||||||||||||||||||||
| ValueListenable<T> valueListenable() => | ||||||||||||||||||||||||
| FlutterValueListenableAdapter<T>(this); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| class FlutterListenableAdapter implements Listenable { | ||||||||||||||||||||||||
| FlutterListenableAdapter(this._listenable); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| final GenUiListenable _listenable; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||
| void addListener(VoidCallback listener) { | ||||||||||||||||||||||||
| _listenable.addListener(listener); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||
| void removeListener(VoidCallback listener) { | ||||||||||||||||||||||||
| _listenable.removeListener(listener); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
Comment on lines
+5
to
+39
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current implementation of the Flutter adapter has several issues that need to be addressed:
import 'package:a2ui_core/a2ui_core.dart' hide VoidCallback;
import 'package:flutter/foundation.dart';
/// Extensions to convert GenUi listenables to Flutter listenables.
extension GenUiListenableFlutterExtension on GenUiListenable {
/// Converts this [GenUiListenable] to a Flutter [Listenable].
Listenable toFlutterListenable() => _FlutterListenableAdapter(this);
}
/// Extensions to convert GenUi value listenables to Flutter value listenables.
extension GenUiValueListenableFlutterExtension<T> on GenUiValueListenable<T> {
/// Converts this [GenUiValueListenable] to a Flutter [ValueListenable].
ValueListenable<T> toFlutterValueListenable() =>
_FlutterValueListenableAdapter<T>(this);
}
class _FlutterListenableAdapter implements Listenable {
_FlutterListenableAdapter(this._listenable);
final GenUiListenable _listenable;
@override
void addListener(VoidCallback listener) => _listenable.addListener(listener);
@override
void removeListener(VoidCallback listener) =>
_listenable.removeListener(listener);
}
class _FlutterValueListenableAdapter<T> extends _FlutterListenableAdapter
implements ValueListenable<T> {
_FlutterValueListenableAdapter(GenUiValueListenable<T> listenable)
: super(listenable);
GenUiValueListenable<T> get _valueListenable =>
_listenable as GenUiValueListenable<T>;
@override
T get value => _valueListenable.value;
}
polina-c marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| class FlutterValueListenableAdapter<T> extends FlutterListenableAdapter | ||||||||||||||||||||||||
| implements ValueListenable<T> { | ||||||||||||||||||||||||
| FlutterValueListenableAdapter(GenUiValueListenable<T> super.listenable); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| GenUiValueListenable<T> get _valueListenable => | ||||||||||||||||||||||||
| _listenable as GenUiValueListenable<T>; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @override | ||||||||||||||||||||||||
| T get value => _valueListenable.value; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| // Copyright 2025 The Flutter Authors. | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
polina-c marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| import 'package:a2ui_core/a2ui_core.dart'; | ||
| import 'package:flutter/foundation.dart'; | ||
| import 'package:flutter_test/flutter_test.dart'; | ||
| import 'package:genui/src/primitives/flutter_listenable.dart'; | ||
|
|
||
| class TestGenUiListenable extends GenUiListenable { | ||
| int addListenerCount = 0; | ||
| int removeListenerCount = 0; | ||
| VoidCallback? lastAddedListener; | ||
| VoidCallback? lastRemovedListener; | ||
|
|
||
| @override | ||
| void addListener(VoidCallback listener) { | ||
| addListenerCount++; | ||
| lastAddedListener = listener; | ||
| } | ||
|
|
||
| @override | ||
| void removeListener(VoidCallback listener) { | ||
| removeListenerCount++; | ||
| lastRemovedListener = listener; | ||
| } | ||
| } | ||
|
|
||
| class TestGenUiValueListenable<T> extends TestGenUiListenable | ||
| implements GenUiValueListenable<T> { | ||
| TestGenUiValueListenable(this.value); | ||
|
|
||
| @override | ||
| T value; | ||
| } | ||
|
|
||
| void main() { | ||
| group('FlutterListenable', () { | ||
| test('adapter registers and unregisters listeners correctly', () { | ||
| final listenable = TestGenUiListenable(); | ||
| final Listenable adapter = listenable.listenable(); | ||
|
|
||
| expect(adapter, isA<Listenable>()); | ||
| expect(adapter, isA<FlutterListenableAdapter>()); | ||
|
|
||
| void listener() {} | ||
|
|
||
| adapter.addListener(listener); | ||
| expect(listenable.addListenerCount, 1); | ||
| expect(listenable.lastAddedListener, listener); | ||
|
|
||
| adapter.removeListener(listener); | ||
| expect(listenable.removeListenerCount, 1); | ||
| expect(listenable.lastRemovedListener, listener); | ||
| }); | ||
|
|
||
| test('valueListenable adapter works correctly', () { | ||
| final valueListenable = TestGenUiValueListenable<int>(42); | ||
| final ValueListenable<int> adapter = valueListenable.valueListenable(); | ||
|
|
||
| expect(adapter, isA<ValueListenable<int>>()); | ||
| expect(adapter, isA<FlutterValueListenableAdapter<int>>()); | ||
|
|
||
| expect(adapter.value, 42); | ||
|
|
||
| void listener() {} | ||
|
|
||
| adapter.addListener(listener); | ||
| expect(valueListenable.addListenerCount, 1); | ||
| expect(valueListenable.lastAddedListener, listener); | ||
|
|
||
| adapter.removeListener(listener); | ||
| expect(valueListenable.removeListenerCount, 1); | ||
| expect(valueListenable.lastRemovedListener, listener); | ||
| }); | ||
| }); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The doc comments for
GenUiValueNotifierand its constructor should refer toGenUiValueNotifierspecifically rather than the base classGenUiChangeNotifierto provide clearer documentation for users of this class.