settings.dart 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import 'package:flutter/widgets.dart';
  2. import 'package:flutter/material.dart' show Icons, ListTile, Material, ThemeMode, SimpleDialogOption, SimpleDialog, showDialog, SwitchListTile;
  3. import 'package:flutter/cupertino.dart' show CupertinoIcons;
  4. import '../l10n/app_localizations.dart';
  5. import '../state/app_state.dart';
  6. import '../ui/adaptive.dart' show AppScaffold, isPlatformAndroid;
  7. import '../helper/url.dart' show launchURL;
  8. import 'package:package_info_plus/package_info_plus.dart' show PackageInfo;
  9. import '../config/routes.dart';
  10. import '../l10n/constants.dart';
  11. import 'package:provider/provider.dart';
  12. /// Settings page.
  13. class SettingsPage extends StatelessWidget {
  14. const SettingsPage({super.key});
  15. @override
  16. Widget build(BuildContext context) {
  17. return AppScaffold(
  18. title: Text(AppLocalizations.of(context)!.settingsTitle),
  19. body: Column(
  20. children: <Widget>[
  21. // App Info
  22. Padding(
  23. padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 15),
  24. child: Row(
  25. mainAxisAlignment: MainAxisAlignment.center,
  26. children: <Widget>[
  27. // App image
  28. Container(
  29. height: 125,
  30. width: 150,
  31. padding: const EdgeInsets.symmetric(horizontal: 10),
  32. child: const DecoratedBox(
  33. decoration: BoxDecoration(
  34. image: DecorationImage(
  35. image: AssetImage('graphics/icon.png'),
  36. ),
  37. ),
  38. ),
  39. ),
  40. SizedBox(
  41. width: 160,
  42. child: Column(
  43. crossAxisAlignment: CrossAxisAlignment.start,
  44. children: <Widget>[
  45. // Name
  46. Padding(
  47. padding: const EdgeInsets.symmetric(vertical: 7),
  48. child: Text(AppLocalizations.of(context)!.appName,
  49. style: const TextStyle(fontSize: 25))),
  50. // Version of app
  51. FutureBuilder(
  52. future: PackageInfo.fromPlatform(),
  53. builder: (BuildContext context,
  54. AsyncSnapshot<PackageInfo> snapshot) {
  55. if (snapshot.hasData) {
  56. return Text(snapshot.data!.version,
  57. style: const TextStyle(fontSize: 14));
  58. }
  59. return Text(AppLocalizations.of(context)!.loading);
  60. },
  61. ),
  62. ],
  63. ),
  64. )
  65. ],
  66. ),
  67. ),
  68. const SizedBox(height: 5),
  69. // List of options
  70. Expanded(
  71. child: Material(
  72. child: ListView(
  73. physics: const NeverScrollableScrollPhysics(),
  74. children: [
  75. // Theme selection
  76. Consumer<AppState>(
  77. builder: (context, appState, _) {
  78. return ListTile(
  79. dense: true,
  80. leading: const Icon(Icons.brightness_6),
  81. title: Text(AppLocalizations.of(context)!.themeMode ?? 'Theme', style: const TextStyle(fontSize: 15)),
  82. subtitle: Text(
  83. appState.themeMode == ThemeMode.system
  84. ? (AppLocalizations.of(context)!.themeSystem ?? 'System')
  85. : appState.themeMode == ThemeMode.light
  86. ? (AppLocalizations.of(context)!.themeLight ?? 'Light')
  87. : (AppLocalizations.of(context)!.themeDark ?? 'Dark'),
  88. ),
  89. onTap: () async {
  90. final selected = await showDialog<ThemeMode>(
  91. context: context,
  92. builder: (context) {
  93. return SimpleDialog(
  94. title: Text(AppLocalizations.of(context)!.themeMode ?? 'Theme'),
  95. children: [
  96. SimpleDialogOption(
  97. child: Text(AppLocalizations.of(context)!.themeSystem ?? 'System'),
  98. onPressed: () => Navigator.pop(context, ThemeMode.system),
  99. ),
  100. SimpleDialogOption(
  101. child: Text(AppLocalizations.of(context)!.themeLight ?? 'Light'),
  102. onPressed: () => Navigator.pop(context, ThemeMode.light),
  103. ),
  104. SimpleDialogOption(
  105. child: Text(AppLocalizations.of(context)!.themeDark ?? 'Dark'),
  106. onPressed: () => Navigator.pop(context, ThemeMode.dark),
  107. ),
  108. ],
  109. );
  110. },
  111. );
  112. if (selected != null) {
  113. appState.setThemeMode(selected);
  114. }
  115. },
  116. );
  117. },
  118. ),
  119. // Screen capture prevention
  120. Consumer<AppState>(
  121. builder: (context, appState, _) {
  122. return SwitchListTile(
  123. dense: true,
  124. secondary: const Icon(Icons.security),
  125. title: Text(AppLocalizations.of(context)!.preventScreenCapture ?? 'Prevent screen capture', style: const TextStyle(fontSize: 15)),
  126. value: appState.screenCapturePrevented,
  127. onChanged: (val) => appState.setScreenCapturePrevented(val),
  128. );
  129. },
  130. ),
  131. // Source
  132. ListTile(
  133. dense: true,
  134. leading: const Icon(Icons.code),
  135. title: Text(AppLocalizations.of(context)!.source,
  136. style: const TextStyle(fontSize: 15)),
  137. onTap: () {
  138. launchURL(Constants.repoUrl);
  139. },
  140. ),
  141. // Acknowledgements
  142. ListTile(
  143. dense: true,
  144. leading: isPlatformAndroid()
  145. ? const Icon(Icons.book)
  146. : const Icon(CupertinoIcons.book),
  147. title: Text(AppLocalizations.of(context)!.licenses,
  148. style: const TextStyle(fontSize: 15)),
  149. onTap: () {
  150. Navigator.of(context)
  151. .pushNamed(AppRoutes.settingAcknowledgements);
  152. },
  153. ),
  154. ],
  155. ),
  156. ),
  157. ),
  158. ],
  159. ),
  160. );
  161. }
  162. }