list_item.dart 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter/services.dart' show Clipboard, ClipboardData;
  3. import '/l10n/app_localizations.dart';
  4. import '../shared/list_item_base.dart' show TotpListItemBase;
  5. /// Home page list item (Android).
  6. class HomeListItem extends TotpListItemBase {
  7. const HomeListItem(super.item, {required Key super.key});
  8. @override
  9. State<StatefulWidget> createState() => _TOTPListItemState();
  10. }
  11. class _TOTPListItemState extends State<HomeListItem>
  12. with SingleTickerProviderStateMixin {
  13. // Dimension of progress indicator
  14. static const double _progressIndicatorDimension = 25;
  15. // Animation for indicator/code
  16. late AnimationController _controller;
  17. late Animation<double> _animation;
  18. // Code that is being displayed
  19. String _code = "";
  20. @override
  21. void dispose() {
  22. _controller.dispose();
  23. super.dispose();
  24. }
  25. @override
  26. void initState() {
  27. super.initState();
  28. // Set initial code
  29. setState(() {
  30. _code = widget.code;
  31. });
  32. // Define animation
  33. // Adapted from progress_indicator_demo.dart from flutter examples
  34. _controller = AnimationController(
  35. duration: Duration(seconds: widget.item.totp.period),
  36. lowerBound: 0.0,
  37. upperBound: 1.0,
  38. vsync: this,
  39. animationBehavior: AnimationBehavior.preserve,
  40. );
  41. _controller.forward(from: widget.indicatorValue);
  42. _animation = Tween(begin: 0.0, end: 1.0).animate(_controller)
  43. ..addStatusListener(
  44. (AnimationStatus status) {
  45. if (status == AnimationStatus.completed) {
  46. // Set code
  47. setState(() {
  48. _code = widget.code;
  49. });
  50. // Reset animation
  51. _controller.forward(from: widget.indicatorValue);
  52. }
  53. },
  54. );
  55. }
  56. @override
  57. Widget build(BuildContext context) {
  58. return Hero(
  59. tag: widget.item.id,
  60. child: Card(
  61. child: InkWell(
  62. onTap: () {
  63. Clipboard.setData(ClipboardData(text: widget.codeUnformatted));
  64. ScaffoldMessenger.of(context).removeCurrentSnackBar();
  65. ScaffoldMessenger.of(context).showSnackBar(SnackBar(
  66. content: Text(AppLocalizations.of(context)!.clipboard),
  67. duration: const Duration(seconds: 1)));
  68. },
  69. child: Padding(
  70. padding: const EdgeInsets.all(25),
  71. child: SingleChildScrollView(
  72. padding: const EdgeInsets.all(5),
  73. child: Stack(
  74. children: <Widget>[
  75. Column(
  76. // Align to start (left)
  77. crossAxisAlignment: CrossAxisAlignment.start,
  78. children: <Widget>[
  79. // Issuer
  80. Text(widget.item.totp.issuer,
  81. style: Theme.of(context).textTheme.titleMedium),
  82. // Generated code
  83. Padding(
  84. padding: const EdgeInsets.symmetric(vertical: 10),
  85. child: Text(_code,
  86. style: Theme.of(context).textTheme.displaySmall)),
  87. // Account name
  88. Text(widget.item.totp.accountName,
  89. style: Theme.of(context).textTheme.bodyMedium)
  90. ],
  91. ),
  92. Positioned(
  93. right: 0,
  94. bottom: 0,
  95. height: _progressIndicatorDimension,
  96. width: _progressIndicatorDimension,
  97. child: AnimatedBuilder(
  98. animation: _animation,
  99. builder: (BuildContext context, Widget? child) =>
  100. CircularProgressIndicator(
  101. value: _animation.value,
  102. strokeWidth: 2.5,
  103. )))
  104. ],
  105. ),
  106. ),
  107. ),
  108. ),
  109. ),
  110. );
  111. }
  112. }