feat(ui): Hide the button when scrolled to the bottom
This commit is contained in:
parent
99021f2668
commit
d38ee1692b
36
flake.lock
36
flake.lock
@ -1,12 +1,15 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"flake-utils": {
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1667395993,
|
"lastModified": 1681202837,
|
||||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -17,16 +20,16 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1669165918,
|
"lastModified": 1676076353,
|
||||||
"narHash": "sha256-hIVruk2+0wmw/Kfzy11rG3q7ev3VTi/IKVODeHcVjFo=",
|
"narHash": "sha256-mdUtE8Tp40cZETwcq5tCwwLqkJVV1ULJQ5GKRtbshag=",
|
||||||
"owner": "NixOS",
|
"owner": "AtaraxiaSjel",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "3b400a525d92e4085e46141ff48cbf89fd89739e",
|
"rev": "5deb99bdccbbb97e7562dee4ba8a3ee3021688e6",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "AtaraxiaSjel",
|
||||||
"ref": "nixpkgs-unstable",
|
"ref": "update/flutter",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@ -36,6 +39,21 @@
|
|||||||
"flake-utils": "flake-utils",
|
"flake-utils": "flake-utils",
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
description = "AniTrack";
|
description = "AniTrack";
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:AtaraxiaSjel/nixpkgs/update/flutter";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
|||||||
on<MangaUpdatedEvent>(_onMangaUpdated);
|
on<MangaUpdatedEvent>(_onMangaUpdated);
|
||||||
on<AnimeRemovedEvent>(_onAnimeRemoved);
|
on<AnimeRemovedEvent>(_onAnimeRemoved);
|
||||||
on<MangaRemovedEvent>(_onMangaRemoved);
|
on<MangaRemovedEvent>(_onMangaRemoved);
|
||||||
|
on<AddButtonVisibilitySetEvent>(_onButtonVisibilityToggled);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal anime state
|
/// Internal anime state
|
||||||
@ -162,6 +163,7 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
|||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
trackingType: event.type,
|
trackingType: event.type,
|
||||||
|
buttonVisibility: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -281,4 +283,12 @@ class AnimeListBloc extends Bloc<AnimeListEvent, AnimeListState> {
|
|||||||
// Update the database
|
// Update the database
|
||||||
await GetIt.I.get<DatabaseService>().deleteManga(event.id);
|
await GetIt.I.get<DatabaseService>().deleteManga(event.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _onButtonVisibilityToggled(AddButtonVisibilitySetEvent event, Emitter<AnimeListState> emit) async {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
buttonVisibility: event.state,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ final _privateConstructorUsedError = UnsupportedError(
|
|||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$AnimeListState {
|
mixin _$AnimeListState {
|
||||||
|
bool get buttonVisibility => throw _privateConstructorUsedError;
|
||||||
List<AnimeTrackingData> get animes => throw _privateConstructorUsedError;
|
List<AnimeTrackingData> get animes => throw _privateConstructorUsedError;
|
||||||
List<MangaTrackingData> get mangas => throw _privateConstructorUsedError;
|
List<MangaTrackingData> get mangas => throw _privateConstructorUsedError;
|
||||||
MediumTrackingState get animeFilterState =>
|
MediumTrackingState get animeFilterState =>
|
||||||
@ -35,7 +36,8 @@ abstract class $AnimeListStateCopyWith<$Res> {
|
|||||||
AnimeListState value, $Res Function(AnimeListState) then) =
|
AnimeListState value, $Res Function(AnimeListState) then) =
|
||||||
_$AnimeListStateCopyWithImpl<$Res>;
|
_$AnimeListStateCopyWithImpl<$Res>;
|
||||||
$Res call(
|
$Res call(
|
||||||
{List<AnimeTrackingData> animes,
|
{bool buttonVisibility,
|
||||||
|
List<AnimeTrackingData> animes,
|
||||||
List<MangaTrackingData> mangas,
|
List<MangaTrackingData> mangas,
|
||||||
MediumTrackingState animeFilterState,
|
MediumTrackingState animeFilterState,
|
||||||
MediumTrackingState mangaFilterState,
|
MediumTrackingState mangaFilterState,
|
||||||
@ -53,6 +55,7 @@ class _$AnimeListStateCopyWithImpl<$Res>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
$Res call({
|
$Res call({
|
||||||
|
Object? buttonVisibility = freezed,
|
||||||
Object? animes = freezed,
|
Object? animes = freezed,
|
||||||
Object? mangas = freezed,
|
Object? mangas = freezed,
|
||||||
Object? animeFilterState = freezed,
|
Object? animeFilterState = freezed,
|
||||||
@ -60,6 +63,10 @@ class _$AnimeListStateCopyWithImpl<$Res>
|
|||||||
Object? trackingType = freezed,
|
Object? trackingType = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(_value.copyWith(
|
return _then(_value.copyWith(
|
||||||
|
buttonVisibility: buttonVisibility == freezed
|
||||||
|
? _value.buttonVisibility
|
||||||
|
: buttonVisibility // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
animes: animes == freezed
|
animes: animes == freezed
|
||||||
? _value.animes
|
? _value.animes
|
||||||
: animes // ignore: cast_nullable_to_non_nullable
|
: animes // ignore: cast_nullable_to_non_nullable
|
||||||
@ -92,7 +99,8 @@ abstract class _$$_AnimeListStateCopyWith<$Res>
|
|||||||
__$$_AnimeListStateCopyWithImpl<$Res>;
|
__$$_AnimeListStateCopyWithImpl<$Res>;
|
||||||
@override
|
@override
|
||||||
$Res call(
|
$Res call(
|
||||||
{List<AnimeTrackingData> animes,
|
{bool buttonVisibility,
|
||||||
|
List<AnimeTrackingData> animes,
|
||||||
List<MangaTrackingData> mangas,
|
List<MangaTrackingData> mangas,
|
||||||
MediumTrackingState animeFilterState,
|
MediumTrackingState animeFilterState,
|
||||||
MediumTrackingState mangaFilterState,
|
MediumTrackingState mangaFilterState,
|
||||||
@ -112,6 +120,7 @@ class __$$_AnimeListStateCopyWithImpl<$Res>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
$Res call({
|
$Res call({
|
||||||
|
Object? buttonVisibility = freezed,
|
||||||
Object? animes = freezed,
|
Object? animes = freezed,
|
||||||
Object? mangas = freezed,
|
Object? mangas = freezed,
|
||||||
Object? animeFilterState = freezed,
|
Object? animeFilterState = freezed,
|
||||||
@ -119,6 +128,10 @@ class __$$_AnimeListStateCopyWithImpl<$Res>
|
|||||||
Object? trackingType = freezed,
|
Object? trackingType = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$_AnimeListState(
|
return _then(_$_AnimeListState(
|
||||||
|
buttonVisibility: buttonVisibility == freezed
|
||||||
|
? _value.buttonVisibility
|
||||||
|
: buttonVisibility // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
animes: animes == freezed
|
animes: animes == freezed
|
||||||
? _value._animes
|
? _value._animes
|
||||||
: animes // ignore: cast_nullable_to_non_nullable
|
: animes // ignore: cast_nullable_to_non_nullable
|
||||||
@ -147,7 +160,8 @@ class __$$_AnimeListStateCopyWithImpl<$Res>
|
|||||||
|
|
||||||
class _$_AnimeListState implements _AnimeListState {
|
class _$_AnimeListState implements _AnimeListState {
|
||||||
_$_AnimeListState(
|
_$_AnimeListState(
|
||||||
{final List<AnimeTrackingData> animes = const [],
|
{this.buttonVisibility = true,
|
||||||
|
final List<AnimeTrackingData> animes = const [],
|
||||||
final List<MangaTrackingData> mangas = const [],
|
final List<MangaTrackingData> mangas = const [],
|
||||||
this.animeFilterState = MediumTrackingState.ongoing,
|
this.animeFilterState = MediumTrackingState.ongoing,
|
||||||
this.mangaFilterState = MediumTrackingState.ongoing,
|
this.mangaFilterState = MediumTrackingState.ongoing,
|
||||||
@ -155,6 +169,9 @@ class _$_AnimeListState implements _AnimeListState {
|
|||||||
: _animes = animes,
|
: _animes = animes,
|
||||||
_mangas = mangas;
|
_mangas = mangas;
|
||||||
|
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final bool buttonVisibility;
|
||||||
final List<AnimeTrackingData> _animes;
|
final List<AnimeTrackingData> _animes;
|
||||||
@override
|
@override
|
||||||
@JsonKey()
|
@JsonKey()
|
||||||
@ -183,7 +200,7 @@ class _$_AnimeListState implements _AnimeListState {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'AnimeListState(animes: $animes, mangas: $mangas, animeFilterState: $animeFilterState, mangaFilterState: $mangaFilterState, trackingType: $trackingType)';
|
return 'AnimeListState(buttonVisibility: $buttonVisibility, animes: $animes, mangas: $mangas, animeFilterState: $animeFilterState, mangaFilterState: $mangaFilterState, trackingType: $trackingType)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -191,6 +208,8 @@ class _$_AnimeListState implements _AnimeListState {
|
|||||||
return identical(this, other) ||
|
return identical(this, other) ||
|
||||||
(other.runtimeType == runtimeType &&
|
(other.runtimeType == runtimeType &&
|
||||||
other is _$_AnimeListState &&
|
other is _$_AnimeListState &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other.buttonVisibility, buttonVisibility) &&
|
||||||
const DeepCollectionEquality().equals(other._animes, _animes) &&
|
const DeepCollectionEquality().equals(other._animes, _animes) &&
|
||||||
const DeepCollectionEquality().equals(other._mangas, _mangas) &&
|
const DeepCollectionEquality().equals(other._mangas, _mangas) &&
|
||||||
const DeepCollectionEquality()
|
const DeepCollectionEquality()
|
||||||
@ -204,6 +223,7 @@ class _$_AnimeListState implements _AnimeListState {
|
|||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(
|
int get hashCode => Object.hash(
|
||||||
runtimeType,
|
runtimeType,
|
||||||
|
const DeepCollectionEquality().hash(buttonVisibility),
|
||||||
const DeepCollectionEquality().hash(_animes),
|
const DeepCollectionEquality().hash(_animes),
|
||||||
const DeepCollectionEquality().hash(_mangas),
|
const DeepCollectionEquality().hash(_mangas),
|
||||||
const DeepCollectionEquality().hash(animeFilterState),
|
const DeepCollectionEquality().hash(animeFilterState),
|
||||||
@ -218,12 +238,15 @@ class _$_AnimeListState implements _AnimeListState {
|
|||||||
|
|
||||||
abstract class _AnimeListState implements AnimeListState {
|
abstract class _AnimeListState implements AnimeListState {
|
||||||
factory _AnimeListState(
|
factory _AnimeListState(
|
||||||
{final List<AnimeTrackingData> animes,
|
{final bool buttonVisibility,
|
||||||
|
final List<AnimeTrackingData> animes,
|
||||||
final List<MangaTrackingData> mangas,
|
final List<MangaTrackingData> mangas,
|
||||||
final MediumTrackingState animeFilterState,
|
final MediumTrackingState animeFilterState,
|
||||||
final MediumTrackingState mangaFilterState,
|
final MediumTrackingState mangaFilterState,
|
||||||
final TrackingMediumType trackingType}) = _$_AnimeListState;
|
final TrackingMediumType trackingType}) = _$_AnimeListState;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get buttonVisibility;
|
||||||
@override
|
@override
|
||||||
List<AnimeTrackingData> get animes;
|
List<AnimeTrackingData> get animes;
|
||||||
@override
|
@override
|
||||||
|
@ -96,3 +96,10 @@ class MangaRemovedEvent extends AnimeListEvent {
|
|||||||
/// The ID of the manga to be removed from the list.
|
/// The ID of the manga to be removed from the list.
|
||||||
final String id;
|
final String id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AddButtonVisibilitySetEvent extends AnimeListEvent {
|
||||||
|
AddButtonVisibilitySetEvent(this.state);
|
||||||
|
|
||||||
|
/// The visibility of the button
|
||||||
|
final bool state;
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ part of 'anime_list_bloc.dart';
|
|||||||
@freezed
|
@freezed
|
||||||
class AnimeListState with _$AnimeListState {
|
class AnimeListState with _$AnimeListState {
|
||||||
factory AnimeListState({
|
factory AnimeListState({
|
||||||
|
@Default(true) bool buttonVisibility,
|
||||||
@Default([]) List<AnimeTrackingData> animes,
|
@Default([]) List<AnimeTrackingData> animes,
|
||||||
@Default([]) List<MangaTrackingData> mangas,
|
@Default([]) List<MangaTrackingData> mangas,
|
||||||
@Default(MediumTrackingState.ongoing) MediumTrackingState animeFilterState,
|
@Default(MediumTrackingState.ongoing) MediumTrackingState animeFilterState,
|
||||||
|
@ -8,9 +8,10 @@ import 'package:anitrack/src/ui/widgets/image.dart';
|
|||||||
import 'package:bottom_bar/bottom_bar.dart';
|
import 'package:bottom_bar/bottom_bar.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:get_it/get_it.dart';
|
||||||
|
|
||||||
class AnimeListPage extends StatelessWidget {
|
class AnimeListPage extends StatefulWidget {
|
||||||
AnimeListPage({
|
const AnimeListPage({
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -21,8 +22,38 @@ class AnimeListPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final PageController _controller = PageController();
|
@override
|
||||||
|
AnimeListPageState createState() => AnimeListPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimeListPageState extends State<AnimeListPage> {
|
||||||
|
final PageController _controller = PageController();
|
||||||
|
final ScrollController _animeScrollController = ScrollController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_animeScrollController.addListener(_onAnimeListScrolled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onAnimeListScrolled() {
|
||||||
|
//print(_animeScrollController.position.maxScrollExtent);
|
||||||
|
final bloc = GetIt.I.get<AnimeListBloc>();
|
||||||
|
if (_animeScrollController.offset + 20 >= _animeScrollController.position.maxScrollExtent) {
|
||||||
|
if (bloc.state.buttonVisibility) {
|
||||||
|
bloc.add(
|
||||||
|
AddButtonVisibilitySetEvent(false),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!bloc.state.buttonVisibility) {
|
||||||
|
bloc.add(
|
||||||
|
AddButtonVisibilitySetEvent(true),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String _getPageTitle(TrackingMediumType type) {
|
String _getPageTitle(TrackingMediumType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TrackingMediumType.anime: return 'Anime';
|
case TrackingMediumType.anime: return 'Anime';
|
||||||
@ -141,6 +172,7 @@ class AnimeListPage extends StatelessWidget {
|
|||||||
childAspectRatio: 120 / (100 * (16 / 9)),
|
childAspectRatio: 120 / (100 * (16 / 9)),
|
||||||
),
|
),
|
||||||
itemCount: state.animes.length,
|
itemCount: state.animes.length,
|
||||||
|
controller: _animeScrollController,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final anime = state.animes[index];
|
final anime = state.animes[index];
|
||||||
return GridItem(
|
return GridItem(
|
||||||
@ -233,14 +265,26 @@ class AnimeListPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: BlocBuilder<AnimeListBloc, AnimeListState>(
|
||||||
onPressed: () {
|
buildWhen: (prev, next) => prev.buttonVisibility != next.buttonVisibility,
|
||||||
context.read<AnimeSearchBloc>().add(
|
builder: (context, state) {
|
||||||
AnimeSearchRequestedEvent(state.trackingType),
|
return AnimatedScale(
|
||||||
|
duration: const Duration(milliseconds: 250),
|
||||||
|
scale: state.buttonVisibility ?
|
||||||
|
1 :
|
||||||
|
0,
|
||||||
|
curve: Curves.easeInOutQuint,
|
||||||
|
child: FloatingActionButton(
|
||||||
|
onPressed: () {
|
||||||
|
context.read<AnimeSearchBloc>().add(
|
||||||
|
AnimeSearchRequestedEvent(state.trackingType),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
tooltip: 'Add new item',
|
||||||
|
child: const Icon(Icons.add),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
tooltip: 'Increment',
|
|
||||||
child: const Icon(Icons.add),
|
|
||||||
),
|
),
|
||||||
bottomNavigationBar: BottomBar(
|
bottomNavigationBar: BottomBar(
|
||||||
selectedIndex: state.trackingType == TrackingMediumType.anime ?
|
selectedIndex: state.trackingType == TrackingMediumType.anime ?
|
||||||
|
@ -35,6 +35,15 @@ class IntegerInputState extends State<IntegerInput> {
|
|||||||
_value = widget.initialValue;
|
_value = widget.initialValue;
|
||||||
_controller.text = _value.toString();
|
_controller.text = _value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _handleSubmit(String text) {
|
||||||
|
final value = int.parse(text);
|
||||||
|
if (value < 0) return;
|
||||||
|
|
||||||
|
_value = value;
|
||||||
|
_controller.text = '$_value';
|
||||||
|
widget.onChanged(_value);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -56,22 +65,23 @@ class IntegerInputState extends State<IntegerInput> {
|
|||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 16,
|
horizontal: 16,
|
||||||
),
|
),
|
||||||
child: TextField(
|
child: Focus(
|
||||||
decoration: InputDecoration(
|
onFocusChange: (hasFocus) {
|
||||||
border: const OutlineInputBorder(),
|
if (!hasFocus) {
|
||||||
labelText: widget.labelText,
|
print('Handle focus loss');
|
||||||
),
|
_handleSubmit(_controller.text);
|
||||||
keyboardType: TextInputType.number,
|
}
|
||||||
textInputAction: TextInputAction.done,
|
|
||||||
controller: _controller,
|
|
||||||
onSubmitted: (text) {
|
|
||||||
final value = int.parse(text);
|
|
||||||
if (value < 0) return;
|
|
||||||
|
|
||||||
_value = value;
|
|
||||||
_controller.text = '$_value';
|
|
||||||
widget.onChanged(_value);
|
|
||||||
},
|
},
|
||||||
|
child: TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
labelText: widget.labelText,
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
textInputAction: TextInputAction.done,
|
||||||
|
controller: _controller,
|
||||||
|
onSubmitted: _handleSubmit,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
372
pubspec.lock
372
pubspec.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user