yubioath-flutter/lib/app/views/app_list_item.dart

156 lines
4.8 KiB
Dart
Raw Normal View History

2023-06-15 18:39:17 +03:00
/*
* Copyright (C) 2023 Yubico.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
2023-06-15 18:39:17 +03:00
import '../../core/state.dart';
import '../models.dart';
import '../shortcuts.dart';
import 'action_popup_menu.dart';
class AppListItem extends ConsumerStatefulWidget {
2023-06-15 18:39:17 +03:00
final Widget? leading;
final String title;
final String? subtitle;
2024-01-09 16:26:52 +03:00
final String? semanticTitle;
2023-06-15 18:39:17 +03:00
final Widget? trailing;
final List<ActionItem> Function(BuildContext context)? buildPopupActions;
final Intent? activationIntent;
2024-01-09 14:50:26 +03:00
final bool selected;
2024-01-09 16:26:52 +03:00
final bool openOnSingleTap;
2023-06-15 18:39:17 +03:00
const AppListItem({
super.key,
this.leading,
required this.title,
2024-01-09 16:26:52 +03:00
this.semanticTitle,
2023-06-15 18:39:17 +03:00
this.subtitle,
this.trailing,
this.buildPopupActions,
this.activationIntent,
2024-01-09 14:50:26 +03:00
this.selected = false,
2024-01-09 16:26:52 +03:00
this.openOnSingleTap = false,
2023-06-15 18:39:17 +03:00
});
@override
ConsumerState<ConsumerStatefulWidget> createState() => _AppListItemState();
2023-06-15 18:39:17 +03:00
}
class _AppListItemState extends ConsumerState<AppListItem> {
2023-06-15 18:39:17 +03:00
final FocusNode _focusNode = FocusNode();
int _lastTap = 0;
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final subtitle = widget.subtitle;
final buildPopupActions = widget.buildPopupActions;
final activationIntent = widget.activationIntent;
final trailing = widget.trailing;
final hasFeature = ref.watch(featureProvider);
2023-06-15 18:39:17 +03:00
2024-01-09 16:26:52 +03:00
return Semantics(
label: widget.semanticTitle ?? widget.title,
child: Shortcuts(
shortcuts: {
LogicalKeySet(LogicalKeyboardKey.enter): const OpenIntent(),
LogicalKeySet(LogicalKeyboardKey.space): const OpenIntent(),
},
child: InkWell(
focusNode: _focusNode,
borderRadius: BorderRadius.circular(30),
onSecondaryTapDown: buildPopupActions == null
? null
: (details) {
final menuItems = buildPopupActions(context)
.where((action) =>
action.feature == null || hasFeature(action.feature!))
.toList();
if (menuItems.isNotEmpty) {
showPopupMenu(
context,
details.globalPosition,
menuItems,
);
}
},
onTap: () {
if (isDesktop && !widget.openOnSingleTap) {
final now = DateTime.now().millisecondsSinceEpoch;
if (now - _lastTap < 500) {
setState(() {
_lastTap = 0;
});
Actions.invoke(context, activationIntent ?? const OpenIntent());
} else {
_focusNode.requestFocus();
setState(() {
_lastTap = now;
});
}
2023-06-15 18:39:17 +03:00
} else {
2024-01-09 16:26:52 +03:00
Actions.invoke<OpenIntent>(context, const OpenIntent());
2023-06-15 18:39:17 +03:00
}
2024-01-09 16:26:52 +03:00
},
onLongPress: activationIntent == null
? null
: () {
Actions.invoke(context, activationIntent);
},
child: Stack(
alignment: AlignmentDirectional.center,
children: [
const SizedBox(height: 64),
ListTile(
selected: widget.selected,
leading: widget.leading,
title: Text(
widget.title,
overflow: TextOverflow.fade,
maxLines: 1,
softWrap: false,
),
subtitle: subtitle != null
? Text(
subtitle,
overflow: TextOverflow.fade,
maxLines: 1,
softWrap: false,
)
: null,
trailing: trailing == null
? null
: Focus(
skipTraversal: true,
descendantsAreTraversable: false,
child: trailing,
),
2023-06-15 18:39:17 +03:00
),
2024-01-09 16:26:52 +03:00
],
),
2023-06-15 18:39:17 +03:00
),
),
);
}
}