mirror of
				https://gitlab.com/famedly/fluffychat.git
				synced 2025-11-04 06:17:26 +01:00 
			
		
		
		
	Merge branch 'krille/switch-to-flutter-lints' into 'main'
refactor: Switch to flutter_lints See merge request famedly/fluffychat!536
This commit is contained in:
		
						commit
						797161c7f2
					
				@ -1,4 +1,4 @@
 | 
			
		||||
include: package:pedantic/analysis_options.yaml
 | 
			
		||||
include: package:flutter_lints/flutter.yaml
 | 
			
		||||
 | 
			
		||||
linter:
 | 
			
		||||
  rules:
 | 
			
		||||
 | 
			
		||||
@ -45,52 +45,52 @@ class AppRoutes {
 | 
			
		||||
  List<VRouteElement> get _mobileRoutes => [
 | 
			
		||||
        VWidget(
 | 
			
		||||
          path: '/rooms',
 | 
			
		||||
          widget: ChatList(),
 | 
			
		||||
          widget: const ChatList(),
 | 
			
		||||
          stackedRoutes: [
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: '/spaces/:roomid',
 | 
			
		||||
              widget: ChatDetails(),
 | 
			
		||||
              widget: const ChatDetails(),
 | 
			
		||||
              stackedRoutes: _chatDetailsRoutes,
 | 
			
		||||
            ),
 | 
			
		||||
            VWidget(path: ':roomid', widget: Chat(), stackedRoutes: [
 | 
			
		||||
            VWidget(path: ':roomid', widget: const Chat(), stackedRoutes: [
 | 
			
		||||
              VWidget(
 | 
			
		||||
                path: 'encryption',
 | 
			
		||||
                widget: ChatEncryptionSettings(),
 | 
			
		||||
                widget: const ChatEncryptionSettings(),
 | 
			
		||||
              ),
 | 
			
		||||
              VWidget(
 | 
			
		||||
                path: 'invite',
 | 
			
		||||
                widget: InvitationSelection(),
 | 
			
		||||
                widget: const InvitationSelection(),
 | 
			
		||||
              ),
 | 
			
		||||
              VWidget(
 | 
			
		||||
                path: 'details',
 | 
			
		||||
                widget: ChatDetails(),
 | 
			
		||||
                widget: const ChatDetails(),
 | 
			
		||||
                stackedRoutes: _chatDetailsRoutes,
 | 
			
		||||
              ),
 | 
			
		||||
            ]),
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: '/settings',
 | 
			
		||||
              widget: Settings(),
 | 
			
		||||
              widget: const Settings(),
 | 
			
		||||
              stackedRoutes: _settingsRoutes,
 | 
			
		||||
            ),
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: '/search',
 | 
			
		||||
              widget: Search(),
 | 
			
		||||
              widget: const Search(),
 | 
			
		||||
            ),
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: '/archive',
 | 
			
		||||
              widget: Archive(),
 | 
			
		||||
              widget: const Archive(),
 | 
			
		||||
            ),
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: '/newprivatechat',
 | 
			
		||||
              widget: NewPrivateChat(),
 | 
			
		||||
              widget: const NewPrivateChat(),
 | 
			
		||||
            ),
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: '/newgroup',
 | 
			
		||||
              widget: NewGroup(),
 | 
			
		||||
              widget: const NewGroup(),
 | 
			
		||||
            ),
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: '/newspace',
 | 
			
		||||
              widget: NewSpace(),
 | 
			
		||||
              widget: const NewSpace(),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
@ -99,64 +99,64 @@ class AppRoutes {
 | 
			
		||||
        VNester(
 | 
			
		||||
          path: '/rooms',
 | 
			
		||||
          widgetBuilder: (child) => TwoColumnLayout(
 | 
			
		||||
            mainView: ChatList(),
 | 
			
		||||
            mainView: const ChatList(),
 | 
			
		||||
            sideView: child,
 | 
			
		||||
          ),
 | 
			
		||||
          buildTransition: _fadeTransition,
 | 
			
		||||
          nestedRoutes: [
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: '',
 | 
			
		||||
              widget: EmptyPage(),
 | 
			
		||||
              widget: const EmptyPage(),
 | 
			
		||||
              buildTransition: _fadeTransition,
 | 
			
		||||
              stackedRoutes: [
 | 
			
		||||
                VWidget(
 | 
			
		||||
                  path: '/spaces/:roomid',
 | 
			
		||||
                  widget: ChatDetails(),
 | 
			
		||||
                  widget: const ChatDetails(),
 | 
			
		||||
                  buildTransition: _fadeTransition,
 | 
			
		||||
                  stackedRoutes: _chatDetailsRoutes,
 | 
			
		||||
                ),
 | 
			
		||||
                VWidget(
 | 
			
		||||
                  path: '/newprivatechat',
 | 
			
		||||
                  widget: NewPrivateChat(),
 | 
			
		||||
                  widget: const NewPrivateChat(),
 | 
			
		||||
                  buildTransition: _fadeTransition,
 | 
			
		||||
                ),
 | 
			
		||||
                VWidget(
 | 
			
		||||
                  path: '/newgroup',
 | 
			
		||||
                  widget: NewGroup(),
 | 
			
		||||
                  widget: const NewGroup(),
 | 
			
		||||
                  buildTransition: _fadeTransition,
 | 
			
		||||
                ),
 | 
			
		||||
                VWidget(
 | 
			
		||||
                  path: '/newspace',
 | 
			
		||||
                  widget: NewSpace(),
 | 
			
		||||
                  widget: const NewSpace(),
 | 
			
		||||
                  buildTransition: _fadeTransition,
 | 
			
		||||
                ),
 | 
			
		||||
                VNester(
 | 
			
		||||
                  path: ':roomid',
 | 
			
		||||
                  widgetBuilder: (child) => SideViewLayout(
 | 
			
		||||
                    mainView: Chat(),
 | 
			
		||||
                    mainView: const Chat(),
 | 
			
		||||
                    sideView: child,
 | 
			
		||||
                  ),
 | 
			
		||||
                  buildTransition: _fadeTransition,
 | 
			
		||||
                  nestedRoutes: [
 | 
			
		||||
                    VWidget(
 | 
			
		||||
                      path: '',
 | 
			
		||||
                      widget: Chat(),
 | 
			
		||||
                      widget: const Chat(),
 | 
			
		||||
                      buildTransition: _fadeTransition,
 | 
			
		||||
                    ),
 | 
			
		||||
                    VWidget(
 | 
			
		||||
                      path: 'encryption',
 | 
			
		||||
                      widget: ChatEncryptionSettings(),
 | 
			
		||||
                      widget: const ChatEncryptionSettings(),
 | 
			
		||||
                      buildTransition: _fadeTransition,
 | 
			
		||||
                    ),
 | 
			
		||||
                    VWidget(
 | 
			
		||||
                      path: 'details',
 | 
			
		||||
                      widget: ChatDetails(),
 | 
			
		||||
                      widget: const ChatDetails(),
 | 
			
		||||
                      buildTransition: _fadeTransition,
 | 
			
		||||
                      stackedRoutes: _chatDetailsRoutes,
 | 
			
		||||
                    ),
 | 
			
		||||
                    VWidget(
 | 
			
		||||
                      path: 'invite',
 | 
			
		||||
                      widget: InvitationSelection(),
 | 
			
		||||
                      widget: const InvitationSelection(),
 | 
			
		||||
                      buildTransition: _fadeTransition,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ],
 | 
			
		||||
@ -167,7 +167,7 @@ class AppRoutes {
 | 
			
		||||
        ),
 | 
			
		||||
        VWidget(
 | 
			
		||||
          path: '/rooms',
 | 
			
		||||
          widget: TwoColumnLayout(
 | 
			
		||||
          widget: const TwoColumnLayout(
 | 
			
		||||
            mainView: ChatList(),
 | 
			
		||||
            sideView: EmptyPage(),
 | 
			
		||||
          ),
 | 
			
		||||
@ -176,14 +176,14 @@ class AppRoutes {
 | 
			
		||||
            VNester(
 | 
			
		||||
              path: '/settings',
 | 
			
		||||
              widgetBuilder: (child) => TwoColumnLayout(
 | 
			
		||||
                mainView: Settings(),
 | 
			
		||||
                mainView: const Settings(),
 | 
			
		||||
                sideView: child,
 | 
			
		||||
              ),
 | 
			
		||||
              buildTransition: _dynamicTransition,
 | 
			
		||||
              nestedRoutes: [
 | 
			
		||||
                VWidget(
 | 
			
		||||
                  path: '',
 | 
			
		||||
                  widget: EmptyPage(),
 | 
			
		||||
                  widget: const EmptyPage(),
 | 
			
		||||
                  buildTransition: _dynamicTransition,
 | 
			
		||||
                  stackedRoutes: _settingsRoutes,
 | 
			
		||||
                ),
 | 
			
		||||
@ -191,7 +191,7 @@ class AppRoutes {
 | 
			
		||||
            ),
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: '/search',
 | 
			
		||||
              widget: TwoColumnLayout(
 | 
			
		||||
              widget: const TwoColumnLayout(
 | 
			
		||||
                mainView: Search(),
 | 
			
		||||
                sideView: EmptyPage(),
 | 
			
		||||
              ),
 | 
			
		||||
@ -199,7 +199,7 @@ class AppRoutes {
 | 
			
		||||
            ),
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: '/archive',
 | 
			
		||||
              widget: TwoColumnLayout(
 | 
			
		||||
              widget: const TwoColumnLayout(
 | 
			
		||||
                mainView: Archive(),
 | 
			
		||||
                sideView: EmptyPage(),
 | 
			
		||||
              ),
 | 
			
		||||
@ -210,25 +210,25 @@ class AppRoutes {
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
  List<VRouteElement> get _homeRoutes => [
 | 
			
		||||
        VWidget(path: '/', widget: LoadingView()),
 | 
			
		||||
        VWidget(path: '/', widget: const LoadingView()),
 | 
			
		||||
        VWidget(
 | 
			
		||||
          path: '/home',
 | 
			
		||||
          widget: HomeserverPicker(),
 | 
			
		||||
          widget: const HomeserverPicker(),
 | 
			
		||||
          buildTransition: _fadeTransition,
 | 
			
		||||
          stackedRoutes: [
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: 'login',
 | 
			
		||||
              widget: Login(),
 | 
			
		||||
              widget: const Login(),
 | 
			
		||||
              buildTransition: _fadeTransition,
 | 
			
		||||
            ),
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: 'signup',
 | 
			
		||||
              widget: SignupPage(),
 | 
			
		||||
              widget: const SignupPage(),
 | 
			
		||||
              buildTransition: _fadeTransition,
 | 
			
		||||
            ),
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: 'logs',
 | 
			
		||||
              widget: LogViewer(),
 | 
			
		||||
              widget: const LogViewer(),
 | 
			
		||||
              buildTransition: _dynamicTransition,
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
@ -238,27 +238,27 @@ class AppRoutes {
 | 
			
		||||
  List<VRouteElement> get _chatDetailsRoutes => [
 | 
			
		||||
        VWidget(
 | 
			
		||||
          path: 'permissions',
 | 
			
		||||
          widget: ChatPermissionsSettings(),
 | 
			
		||||
          widget: const ChatPermissionsSettings(),
 | 
			
		||||
          buildTransition: _dynamicTransition,
 | 
			
		||||
        ),
 | 
			
		||||
        VWidget(
 | 
			
		||||
          path: 'invite',
 | 
			
		||||
          widget: InvitationSelection(),
 | 
			
		||||
          widget: const InvitationSelection(),
 | 
			
		||||
          buildTransition: _dynamicTransition,
 | 
			
		||||
        ),
 | 
			
		||||
        VWidget(
 | 
			
		||||
          path: 'multiple_emotes',
 | 
			
		||||
          widget: MultipleEmotesSettings(),
 | 
			
		||||
          widget: const MultipleEmotesSettings(),
 | 
			
		||||
          buildTransition: _dynamicTransition,
 | 
			
		||||
        ),
 | 
			
		||||
        VWidget(
 | 
			
		||||
          path: 'emotes',
 | 
			
		||||
          widget: EmotesSettings(),
 | 
			
		||||
          widget: const EmotesSettings(),
 | 
			
		||||
          buildTransition: _dynamicTransition,
 | 
			
		||||
        ),
 | 
			
		||||
        VWidget(
 | 
			
		||||
          path: 'emotes/:state_key',
 | 
			
		||||
          widget: EmotesSettings(),
 | 
			
		||||
          widget: const EmotesSettings(),
 | 
			
		||||
          buildTransition: _dynamicTransition,
 | 
			
		||||
        ),
 | 
			
		||||
      ];
 | 
			
		||||
@ -266,49 +266,49 @@ class AppRoutes {
 | 
			
		||||
  List<VRouteElement> get _settingsRoutes => [
 | 
			
		||||
        VWidget(
 | 
			
		||||
          path: 'notifications',
 | 
			
		||||
          widget: SettingsNotifications(),
 | 
			
		||||
          widget: const SettingsNotifications(),
 | 
			
		||||
          buildTransition: _dynamicTransition,
 | 
			
		||||
        ),
 | 
			
		||||
        VWidget(
 | 
			
		||||
          path: 'chat',
 | 
			
		||||
          widget: SettingsChat(),
 | 
			
		||||
          widget: const SettingsChat(),
 | 
			
		||||
          buildTransition: _dynamicTransition,
 | 
			
		||||
          stackedRoutes: [
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: 'emotes',
 | 
			
		||||
              widget: EmotesSettings(),
 | 
			
		||||
              widget: const EmotesSettings(),
 | 
			
		||||
              buildTransition: _dynamicTransition,
 | 
			
		||||
            ),
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: 'style',
 | 
			
		||||
              widget: SettingsStyle(),
 | 
			
		||||
              widget: const SettingsStyle(),
 | 
			
		||||
              buildTransition: _dynamicTransition,
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        VWidget(
 | 
			
		||||
          path: 'account',
 | 
			
		||||
          widget: SettingsAccount(),
 | 
			
		||||
          widget: const SettingsAccount(),
 | 
			
		||||
          buildTransition: _dynamicTransition,
 | 
			
		||||
          stackedRoutes: [
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: 'devices',
 | 
			
		||||
              widget: DevicesSettings(),
 | 
			
		||||
              widget: const DevicesSettings(),
 | 
			
		||||
              buildTransition: _dynamicTransition,
 | 
			
		||||
            ),
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: 'add',
 | 
			
		||||
              widget: HomeserverPicker(),
 | 
			
		||||
              widget: const HomeserverPicker(),
 | 
			
		||||
              buildTransition: _fadeTransition,
 | 
			
		||||
              stackedRoutes: [
 | 
			
		||||
                VWidget(
 | 
			
		||||
                  path: 'login',
 | 
			
		||||
                  widget: Login(),
 | 
			
		||||
                  widget: const Login(),
 | 
			
		||||
                  buildTransition: _fadeTransition,
 | 
			
		||||
                ),
 | 
			
		||||
                VWidget(
 | 
			
		||||
                  path: 'signup',
 | 
			
		||||
                  widget: SignupPage(),
 | 
			
		||||
                  widget: const SignupPage(),
 | 
			
		||||
                  buildTransition: _fadeTransition,
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
@ -317,31 +317,31 @@ class AppRoutes {
 | 
			
		||||
        ),
 | 
			
		||||
        VWidget(
 | 
			
		||||
          path: 'security',
 | 
			
		||||
          widget: SettingsSecurity(),
 | 
			
		||||
          widget: const SettingsSecurity(),
 | 
			
		||||
          buildTransition: _dynamicTransition,
 | 
			
		||||
          stackedRoutes: [
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: 'ignorelist',
 | 
			
		||||
              widget: SettingsIgnoreList(),
 | 
			
		||||
              widget: const SettingsIgnoreList(),
 | 
			
		||||
              buildTransition: _dynamicTransition,
 | 
			
		||||
            ),
 | 
			
		||||
            VWidget(
 | 
			
		||||
              path: '3pid',
 | 
			
		||||
              widget: Settings3Pid(),
 | 
			
		||||
              widget: const Settings3Pid(),
 | 
			
		||||
              buildTransition: _dynamicTransition,
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        VWidget(
 | 
			
		||||
          path: 'logs',
 | 
			
		||||
          widget: LogViewer(),
 | 
			
		||||
          widget: const LogViewer(),
 | 
			
		||||
          buildTransition: _dynamicTransition,
 | 
			
		||||
        ),
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
  final _fadeTransition = (animation1, _, child) =>
 | 
			
		||||
      FadeTransition(opacity: animation1, child: child);
 | 
			
		||||
 | 
			
		||||
  FadeTransition Function(dynamic, dynamic, dynamic) get _dynamicTransition =>
 | 
			
		||||
      columnMode ? _fadeTransition : null;
 | 
			
		||||
 | 
			
		||||
  FadeTransition _fadeTransition(animation1, _, child) =>
 | 
			
		||||
      FadeTransition(opacity: animation1, child: child);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -14,8 +14,8 @@ abstract class FluffyThemes {
 | 
			
		||||
  static const fallbackTextStyle =
 | 
			
		||||
      TextStyle(fontFamily: 'Roboto', fontFamilyFallback: ['NotoEmoji']);
 | 
			
		||||
 | 
			
		||||
  static var fallback_text_theme = PlatformInfos.isDesktop
 | 
			
		||||
      ? TextTheme(
 | 
			
		||||
  static var fallbackTextTheme = PlatformInfos.isDesktop
 | 
			
		||||
      ? const TextTheme(
 | 
			
		||||
          bodyText1: fallbackTextStyle,
 | 
			
		||||
          bodyText2: fallbackTextStyle,
 | 
			
		||||
          button: fallbackTextStyle,
 | 
			
		||||
@ -29,12 +29,12 @@ abstract class FluffyThemes {
 | 
			
		||||
          headline6: fallbackTextStyle,
 | 
			
		||||
          subtitle1: fallbackTextStyle,
 | 
			
		||||
          subtitle2: fallbackTextStyle)
 | 
			
		||||
      : TextTheme();
 | 
			
		||||
      : const TextTheme();
 | 
			
		||||
 | 
			
		||||
  static ThemeData light = ThemeData(
 | 
			
		||||
    visualDensity: VisualDensity.standard,
 | 
			
		||||
    primaryColorDark: Colors.white,
 | 
			
		||||
    primaryColorLight: Color(0xff121212),
 | 
			
		||||
    primaryColorLight: const Color(0xff121212),
 | 
			
		||||
    brightness: Brightness.light,
 | 
			
		||||
    primaryColor: AppConfig.primaryColor,
 | 
			
		||||
    colorScheme: ThemeData.light().colorScheme.copyWith(
 | 
			
		||||
@ -43,11 +43,11 @@ abstract class FluffyThemes {
 | 
			
		||||
          secondaryVariant: AppConfig.secondaryColor,
 | 
			
		||||
        ),
 | 
			
		||||
    backgroundColor: Colors.white,
 | 
			
		||||
    secondaryHeaderColor: Color(0xffeeeffe),
 | 
			
		||||
    secondaryHeaderColor: const Color(0xffeeeffe),
 | 
			
		||||
    scaffoldBackgroundColor: Colors.white,
 | 
			
		||||
    textTheme: Typography.material2018().black.merge(fallback_text_theme),
 | 
			
		||||
    snackBarTheme: SnackBarThemeData(behavior: SnackBarBehavior.floating),
 | 
			
		||||
    pageTransitionsTheme: PageTransitionsTheme(
 | 
			
		||||
    textTheme: Typography.material2018().black.merge(fallbackTextTheme),
 | 
			
		||||
    snackBarTheme: const SnackBarThemeData(behavior: SnackBarBehavior.floating),
 | 
			
		||||
    pageTransitionsTheme: const PageTransitionsTheme(
 | 
			
		||||
      builders: {
 | 
			
		||||
        TargetPlatform.fuchsia: ZoomPageTransitionsBuilder(),
 | 
			
		||||
        TargetPlatform.android: ZoomPageTransitionsBuilder(),
 | 
			
		||||
@ -75,7 +75,7 @@ abstract class FluffyThemes {
 | 
			
		||||
        borderRadius: BorderRadius.circular(AppConfig.borderRadius),
 | 
			
		||||
      ),
 | 
			
		||||
    ),
 | 
			
		||||
    floatingActionButtonTheme: FloatingActionButtonThemeData(
 | 
			
		||||
    floatingActionButtonTheme: const FloatingActionButtonThemeData(
 | 
			
		||||
      backgroundColor: AppConfig.primaryColor,
 | 
			
		||||
      foregroundColor: Colors.white,
 | 
			
		||||
    ),
 | 
			
		||||
@ -86,7 +86,7 @@ abstract class FluffyThemes {
 | 
			
		||||
        shape: RoundedRectangleBorder(
 | 
			
		||||
          borderRadius: BorderRadius.circular(AppConfig.borderRadius),
 | 
			
		||||
        ),
 | 
			
		||||
        padding: EdgeInsets.all(12),
 | 
			
		||||
        padding: const EdgeInsets.all(12),
 | 
			
		||||
      ),
 | 
			
		||||
    ),
 | 
			
		||||
    cardTheme: CardTheme(
 | 
			
		||||
@ -108,7 +108,7 @@ abstract class FluffyThemes {
 | 
			
		||||
      filled: true,
 | 
			
		||||
      fillColor: lighten(AppConfig.primaryColor, .51),
 | 
			
		||||
    ),
 | 
			
		||||
    appBarTheme: AppBarTheme(
 | 
			
		||||
    appBarTheme: const AppBarTheme(
 | 
			
		||||
      elevation: 2,
 | 
			
		||||
      systemOverlayStyle: SystemUiOverlayStyle.dark,
 | 
			
		||||
      backgroundColor: Colors.white,
 | 
			
		||||
@ -122,10 +122,10 @@ abstract class FluffyThemes {
 | 
			
		||||
 | 
			
		||||
  static ThemeData dark = ThemeData.dark().copyWith(
 | 
			
		||||
    visualDensity: VisualDensity.standard,
 | 
			
		||||
    primaryColorDark: Color(0xff121212),
 | 
			
		||||
    primaryColorDark: const Color(0xff121212),
 | 
			
		||||
    primaryColorLight: Colors.white,
 | 
			
		||||
    primaryColor: AppConfig.primaryColor,
 | 
			
		||||
    errorColor: Color(0xFFCF6679),
 | 
			
		||||
    errorColor: const Color(0xFFCF6679),
 | 
			
		||||
    backgroundColor: Colors.black,
 | 
			
		||||
    scaffoldBackgroundColor: Colors.black,
 | 
			
		||||
    colorScheme: ThemeData.dark().colorScheme.copyWith(
 | 
			
		||||
@ -133,8 +133,8 @@ abstract class FluffyThemes {
 | 
			
		||||
          secondary: AppConfig.primaryColorLight,
 | 
			
		||||
          secondaryVariant: AppConfig.secondaryColor,
 | 
			
		||||
        ),
 | 
			
		||||
    secondaryHeaderColor: Color(0xff232543),
 | 
			
		||||
    textTheme: Typography.material2018().white.merge(fallback_text_theme),
 | 
			
		||||
    secondaryHeaderColor: const Color(0xff232543),
 | 
			
		||||
    textTheme: Typography.material2018().white.merge(fallbackTextTheme),
 | 
			
		||||
    dialogTheme: DialogTheme(
 | 
			
		||||
      shape: RoundedRectangleBorder(
 | 
			
		||||
        borderRadius: BorderRadius.circular(AppConfig.borderRadius),
 | 
			
		||||
@ -152,7 +152,7 @@ abstract class FluffyThemes {
 | 
			
		||||
      ),
 | 
			
		||||
      clipBehavior: Clip.hardEdge,
 | 
			
		||||
    ),
 | 
			
		||||
    pageTransitionsTheme: PageTransitionsTheme(
 | 
			
		||||
    pageTransitionsTheme: const PageTransitionsTheme(
 | 
			
		||||
      builders: {
 | 
			
		||||
        TargetPlatform.fuchsia: ZoomPageTransitionsBuilder(),
 | 
			
		||||
        TargetPlatform.android: ZoomPageTransitionsBuilder(),
 | 
			
		||||
@ -162,7 +162,7 @@ abstract class FluffyThemes {
 | 
			
		||||
        TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
 | 
			
		||||
      },
 | 
			
		||||
    ),
 | 
			
		||||
    floatingActionButtonTheme: FloatingActionButtonThemeData(
 | 
			
		||||
    floatingActionButtonTheme: const FloatingActionButtonThemeData(
 | 
			
		||||
      backgroundColor: AppConfig.primaryColor,
 | 
			
		||||
      foregroundColor: Colors.white,
 | 
			
		||||
    ),
 | 
			
		||||
@ -192,11 +192,11 @@ abstract class FluffyThemes {
 | 
			
		||||
        shape: RoundedRectangleBorder(
 | 
			
		||||
          borderRadius: BorderRadius.circular(AppConfig.borderRadius),
 | 
			
		||||
        ),
 | 
			
		||||
        padding: EdgeInsets.all(12),
 | 
			
		||||
        padding: const EdgeInsets.all(12),
 | 
			
		||||
      ),
 | 
			
		||||
    ),
 | 
			
		||||
    snackBarTheme: SnackBarThemeData(behavior: SnackBarBehavior.floating),
 | 
			
		||||
    appBarTheme: AppBarTheme(
 | 
			
		||||
    snackBarTheme: const SnackBarThemeData(behavior: SnackBarBehavior.floating),
 | 
			
		||||
    appBarTheme: const AppBarTheme(
 | 
			
		||||
      elevation: 2,
 | 
			
		||||
      systemOverlayStyle: SystemUiOverlayStyle.light,
 | 
			
		||||
      backgroundColor: Color(0xff1D1D1D),
 | 
			
		||||
 | 
			
		||||
@ -53,7 +53,7 @@ void main() async {
 | 
			
		||||
              clients: clients,
 | 
			
		||||
              queryParameters: queryParameters,
 | 
			
		||||
            ),
 | 
			
		||||
            lockScreen: LockScreen(),
 | 
			
		||||
            lockScreen: const LockScreen(),
 | 
			
		||||
            enabled: false,
 | 
			
		||||
          )
 | 
			
		||||
        : FluffyChatApp(clients: clients, queryParameters: queryParameters)),
 | 
			
		||||
@ -117,7 +117,7 @@ class _FluffyChatAppState extends State<FluffyChatApp> {
 | 
			
		||||
          }
 | 
			
		||||
          return VRouter(
 | 
			
		||||
            key: _router,
 | 
			
		||||
            title: '${AppConfig.applicationName}',
 | 
			
		||||
            title: AppConfig.applicationName,
 | 
			
		||||
            theme: theme,
 | 
			
		||||
            scrollBehavior: CustomScrollBehavior(),
 | 
			
		||||
            logs: kReleaseMode ? VLogs.none : VLogs.info,
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,8 @@ import 'package:future_loading_dialog/future_loading_dialog.dart';
 | 
			
		||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
 | 
			
		||||
 | 
			
		||||
class Archive extends StatefulWidget {
 | 
			
		||||
  const Archive({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  ArchiveController createState() => ArchiveController();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -71,8 +71,8 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
 | 
			
		||||
    _wipe ??= widget.wipe;
 | 
			
		||||
    final buttons = <AdaptiveFlatButton>[];
 | 
			
		||||
    Widget body = PlatformInfos.isCupertinoStyle
 | 
			
		||||
        ? CupertinoActivityIndicator()
 | 
			
		||||
        : LinearProgressIndicator();
 | 
			
		||||
        ? const CupertinoActivityIndicator()
 | 
			
		||||
        : const LinearProgressIndicator();
 | 
			
		||||
    titleText = L10n.of(context).loadingPleaseWait;
 | 
			
		||||
 | 
			
		||||
    if (bootstrap == null) {
 | 
			
		||||
@ -96,7 +96,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
 | 
			
		||||
        appBar: AppBar(
 | 
			
		||||
          centerTitle: true,
 | 
			
		||||
          leading: IconButton(
 | 
			
		||||
            icon: Icon(Icons.close),
 | 
			
		||||
            icon: const Icon(Icons.close),
 | 
			
		||||
            onPressed: Navigator.of(context).pop,
 | 
			
		||||
          ),
 | 
			
		||||
          title: Text(L10n.of(context).securityKey),
 | 
			
		||||
@ -104,7 +104,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
 | 
			
		||||
        body: Center(
 | 
			
		||||
          child: ConstrainedBox(
 | 
			
		||||
            constraints:
 | 
			
		||||
                BoxConstraints(maxWidth: FluffyThemes.columnWidth * 1.5),
 | 
			
		||||
                const BoxConstraints(maxWidth: FluffyThemes.columnWidth * 1.5),
 | 
			
		||||
            child: ListView(
 | 
			
		||||
              padding: const EdgeInsets.all(16.0),
 | 
			
		||||
              children: [
 | 
			
		||||
@ -116,7 +116,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
 | 
			
		||||
                ),
 | 
			
		||||
                const SizedBox(height: 16),
 | 
			
		||||
                ElevatedButton.icon(
 | 
			
		||||
                  icon: Icon(Icons.copy_outlined),
 | 
			
		||||
                  icon: const Icon(Icons.copy_outlined),
 | 
			
		||||
                  label: Text(L10n.of(context).copyToClipboard),
 | 
			
		||||
                  onPressed: () => Clipboard.setData(ClipboardData(text: key)),
 | 
			
		||||
                ),
 | 
			
		||||
@ -126,7 +126,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
 | 
			
		||||
                    primary: Theme.of(context).secondaryHeaderColor,
 | 
			
		||||
                    onPrimary: Theme.of(context).primaryColor,
 | 
			
		||||
                  ),
 | 
			
		||||
                  icon: Icon(Icons.check_outlined),
 | 
			
		||||
                  icon: const Icon(Icons.check_outlined),
 | 
			
		||||
                  label: Text(L10n.of(context).iWroteDownTheKey),
 | 
			
		||||
                  onPressed: () => setState(() => _recoveryKeyStored = true),
 | 
			
		||||
                ),
 | 
			
		||||
@ -170,7 +170,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
 | 
			
		||||
            appBar: AppBar(
 | 
			
		||||
              centerTitle: true,
 | 
			
		||||
              leading: IconButton(
 | 
			
		||||
                icon: Icon(Icons.close),
 | 
			
		||||
                icon: const Icon(Icons.close),
 | 
			
		||||
                onPressed: Navigator.of(context).pop,
 | 
			
		||||
              ),
 | 
			
		||||
              title: Text(L10n.of(context).pleaseEnterSecurityKey),
 | 
			
		||||
@ -178,7 +178,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
 | 
			
		||||
            body: Center(
 | 
			
		||||
              child: ConstrainedBox(
 | 
			
		||||
                constraints:
 | 
			
		||||
                    BoxConstraints(maxWidth: FluffyThemes.columnWidth * 1.5),
 | 
			
		||||
                    const BoxConstraints(maxWidth: FluffyThemes.columnWidth * 1.5),
 | 
			
		||||
                child: ListView(
 | 
			
		||||
                  padding: const EdgeInsets.all(16.0),
 | 
			
		||||
                  children: [
 | 
			
		||||
@ -199,7 +199,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
 | 
			
		||||
                    ),
 | 
			
		||||
                    const SizedBox(height: 16),
 | 
			
		||||
                    ElevatedButton.icon(
 | 
			
		||||
                        icon: Icon(Icons.lock_open_outlined),
 | 
			
		||||
                        icon: const Icon(Icons.lock_open_outlined),
 | 
			
		||||
                        label: Text(L10n.of(context).unlockChatBackup),
 | 
			
		||||
                        onPressed: () async {
 | 
			
		||||
                          setState(() {
 | 
			
		||||
@ -222,12 +222,12 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
 | 
			
		||||
                        }),
 | 
			
		||||
                    const SizedBox(height: 16),
 | 
			
		||||
                    Row(children: [
 | 
			
		||||
                      Expanded(child: Divider()),
 | 
			
		||||
                      const Expanded(child: Divider()),
 | 
			
		||||
                      Padding(
 | 
			
		||||
                        padding: const EdgeInsets.all(12.0),
 | 
			
		||||
                        child: Text(L10n.of(context).or),
 | 
			
		||||
                      ),
 | 
			
		||||
                      Expanded(child: Divider()),
 | 
			
		||||
                      const Expanded(child: Divider()),
 | 
			
		||||
                    ]),
 | 
			
		||||
                    const SizedBox(height: 16),
 | 
			
		||||
                    ElevatedButton.icon(
 | 
			
		||||
@ -235,7 +235,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
 | 
			
		||||
                        primary: Theme.of(context).secondaryHeaderColor,
 | 
			
		||||
                        onPrimary: Theme.of(context).primaryColor,
 | 
			
		||||
                      ),
 | 
			
		||||
                      icon: Icon(Icons.transfer_within_a_station_outlined),
 | 
			
		||||
                      icon: const Icon(Icons.transfer_within_a_station_outlined),
 | 
			
		||||
                      label: Text(L10n.of(context).transferFromAnotherDevice),
 | 
			
		||||
                      onPressed: () async {
 | 
			
		||||
                        final req = await showFutureLoadingDialog(
 | 
			
		||||
@ -256,7 +256,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
 | 
			
		||||
                        primary: Theme.of(context).secondaryHeaderColor,
 | 
			
		||||
                        onPrimary: Colors.red,
 | 
			
		||||
                      ),
 | 
			
		||||
                      icon: Icon(Icons.delete_outlined),
 | 
			
		||||
                      icon: const Icon(Icons.delete_outlined),
 | 
			
		||||
                      label: Text(L10n.of(context).securityKeyLost),
 | 
			
		||||
                      onPressed: () async {
 | 
			
		||||
                        if (OkCancelResult.ok ==
 | 
			
		||||
@ -305,7 +305,7 @@ class _BootstrapDialogState extends State<BootstrapDialog> {
 | 
			
		||||
          break;
 | 
			
		||||
        case BootstrapState.error:
 | 
			
		||||
          titleText = L10n.of(context).oopsSomethingWentWrong;
 | 
			
		||||
          body = Icon(Icons.error_outline, color: Colors.red, size: 40);
 | 
			
		||||
          body = const Icon(Icons.error_outline, color: Colors.red, size: 40);
 | 
			
		||||
          buttons.add(AdaptiveFlatButton(
 | 
			
		||||
            label: L10n.of(context).close,
 | 
			
		||||
            onPressed: () =>
 | 
			
		||||
 | 
			
		||||
@ -37,7 +37,7 @@ import '../utils/account_bundles.dart';
 | 
			
		||||
class Chat extends StatefulWidget {
 | 
			
		||||
  final Widget sideView;
 | 
			
		||||
 | 
			
		||||
  Chat({Key key, this.sideView}) : super(key: key);
 | 
			
		||||
  const Chat({Key key, this.sideView}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  ChatController createState() => ChatController();
 | 
			
		||||
@ -269,7 +269,7 @@ class ChatController extends State<Chat> {
 | 
			
		||||
        parseCommands: parseCommands);
 | 
			
		||||
    sendController.value = TextEditingValue(
 | 
			
		||||
      text: pendingText,
 | 
			
		||||
      selection: TextSelection.collapsed(offset: 0),
 | 
			
		||||
      selection: const TextSelection.collapsed(offset: 0),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    setState(() {
 | 
			
		||||
@ -360,7 +360,7 @@ class ChatController extends State<Chat> {
 | 
			
		||||
    final result = await showDialog<String>(
 | 
			
		||||
      context: context,
 | 
			
		||||
      useRootNavigator: false,
 | 
			
		||||
      builder: (c) => RecordingDialog(),
 | 
			
		||||
      builder: (c) => const RecordingDialog(),
 | 
			
		||||
    );
 | 
			
		||||
    if (result == null) return;
 | 
			
		||||
    final audioFile = File(result);
 | 
			
		||||
@ -705,11 +705,11 @@ class ChatController extends State<Chat> {
 | 
			
		||||
 | 
			
		||||
  int findChildIndexCallback(Key key, Map<String, int> thisEventsKeyMap) {
 | 
			
		||||
    // this method is called very often. As such, it has to be optimized for speed.
 | 
			
		||||
    if (!(key is ValueKey)) {
 | 
			
		||||
    if (key is! ValueKey) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
    final eventId = (key as ValueKey).value;
 | 
			
		||||
    if (!(eventId is String)) {
 | 
			
		||||
    if (eventId is! String) {
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
    // first fetch the last index the event was at
 | 
			
		||||
@ -764,18 +764,18 @@ class ChatController extends State<Chat> {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    typingCoolDown?.cancel();
 | 
			
		||||
    typingCoolDown = Timer(Duration(seconds: 2), () {
 | 
			
		||||
    typingCoolDown = Timer(const Duration(seconds: 2), () {
 | 
			
		||||
      typingCoolDown = null;
 | 
			
		||||
      currentlyTyping = false;
 | 
			
		||||
      room.setTyping(false);
 | 
			
		||||
    });
 | 
			
		||||
    typingTimeout ??= Timer(Duration(seconds: 30), () {
 | 
			
		||||
    typingTimeout ??= Timer(const Duration(seconds: 30), () {
 | 
			
		||||
      typingTimeout = null;
 | 
			
		||||
      currentlyTyping = false;
 | 
			
		||||
    });
 | 
			
		||||
    if (!currentlyTyping) {
 | 
			
		||||
      currentlyTyping = true;
 | 
			
		||||
      room.setTyping(true, timeout: Duration(seconds: 30).inMilliseconds);
 | 
			
		||||
      room.setTyping(true, timeout: const Duration(seconds: 30).inMilliseconds);
 | 
			
		||||
    }
 | 
			
		||||
    setState(() => inputText = text);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@ import 'package:vrouter/vrouter.dart';
 | 
			
		||||
enum AliasActions { copy, delete, setCanonical }
 | 
			
		||||
 | 
			
		||||
class ChatDetails extends StatefulWidget {
 | 
			
		||||
  const ChatDetails();
 | 
			
		||||
  const ChatDetails({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  ChatDetailsController createState() => ChatDetailsController();
 | 
			
		||||
@ -297,7 +297,7 @@ class ChatDetailsController extends State<ChatDetails> {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    members ??= Matrix.of(context).client.getRoomById(roomId).getParticipants();
 | 
			
		||||
    return Container(
 | 
			
		||||
    return SizedBox(
 | 
			
		||||
      width: 360.0,
 | 
			
		||||
      child: ChatDetailsView(this),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@ -16,17 +16,19 @@ class ChatEncryptionSettings extends StatefulWidget {
 | 
			
		||||
 | 
			
		||||
class ChatEncryptionSettingsController extends State<ChatEncryptionSettings> {
 | 
			
		||||
  String get roomId => VRouter.of(context).pathParameters['roomid'];
 | 
			
		||||
 | 
			
		||||
  Future<void> unblock(DeviceKeys key) async {
 | 
			
		||||
    if (key.blocked) {
 | 
			
		||||
      await key.setBlocked(false);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> onSelected(
 | 
			
		||||
      BuildContext context, String action, DeviceKeys key) async {
 | 
			
		||||
    final room = Matrix.of(context).client.getRoomById(roomId);
 | 
			
		||||
    final unblock = () async {
 | 
			
		||||
      if (key.blocked) {
 | 
			
		||||
        await key.setBlocked(false);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    switch (action) {
 | 
			
		||||
      case 'verify':
 | 
			
		||||
        await unblock();
 | 
			
		||||
        await unblock(key);
 | 
			
		||||
        final req = key.startVerification();
 | 
			
		||||
        req.onUpdate = () {
 | 
			
		||||
          if (req.state == KeyVerificationState.done) {
 | 
			
		||||
@ -36,7 +38,7 @@ class ChatEncryptionSettingsController extends State<ChatEncryptionSettings> {
 | 
			
		||||
        await KeyVerificationDialog(request: req).show(context);
 | 
			
		||||
        break;
 | 
			
		||||
      case 'verify_user':
 | 
			
		||||
        await unblock();
 | 
			
		||||
        await unblock(key);
 | 
			
		||||
        final req =
 | 
			
		||||
            await room.client.userDeviceKeys[key.userId].startVerification();
 | 
			
		||||
        req.onUpdate = () {
 | 
			
		||||
@ -54,7 +56,7 @@ class ChatEncryptionSettingsController extends State<ChatEncryptionSettings> {
 | 
			
		||||
        setState(() => null);
 | 
			
		||||
        break;
 | 
			
		||||
      case 'unblock':
 | 
			
		||||
        await unblock();
 | 
			
		||||
        await unblock(key);
 | 
			
		||||
        setState(() => null);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,8 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
 | 
			
		||||
import '../widgets/matrix.dart';
 | 
			
		||||
 | 
			
		||||
class DevicesSettings extends StatefulWidget {
 | 
			
		||||
  const DevicesSettings({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  DevicesSettingsController createState() => DevicesSettingsController();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,8 @@ import 'package:uni_links/uni_links.dart';
 | 
			
		||||
import '../main.dart';
 | 
			
		||||
 | 
			
		||||
class HomeserverPicker extends StatefulWidget {
 | 
			
		||||
  const HomeserverPicker({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  HomeserverPickerController createState() => HomeserverPickerController();
 | 
			
		||||
}
 | 
			
		||||
@ -37,7 +39,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
 | 
			
		||||
    this.domain = domain;
 | 
			
		||||
    _coolDown?.cancel();
 | 
			
		||||
    if (domain.isNotEmpty) {
 | 
			
		||||
      _coolDown = Timer(Duration(seconds: 1), checkHomeserverAction);
 | 
			
		||||
      _coolDown = Timer(const Duration(seconds: 1), checkHomeserverAction);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -133,7 +135,7 @@ class HomeserverPickerController extends State<HomeserverPicker> {
 | 
			
		||||
        AppConfig.jitsiInstance = jitsi;
 | 
			
		||||
      }
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      setState(() => error = '${(e as Object).toLocalizedString(context)}');
 | 
			
		||||
      setState(() => error = (e as Object).toLocalizedString(context));
 | 
			
		||||
    } finally {
 | 
			
		||||
      if (mounted) {
 | 
			
		||||
        setState(() => isLoading = false);
 | 
			
		||||
 | 
			
		||||
@ -70,7 +70,7 @@ class InvitationSelectionController extends State<InvitationSelection> {
 | 
			
		||||
  void searchUserWithCoolDown(String text) async {
 | 
			
		||||
    coolDown?.cancel();
 | 
			
		||||
    coolDown = Timer(
 | 
			
		||||
      Duration(seconds: 1),
 | 
			
		||||
      const Duration(seconds: 1),
 | 
			
		||||
      () => searchUser(context, text),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -31,9 +31,10 @@ class KeyVerificationDialog extends StatefulWidget {
 | 
			
		||||
 | 
			
		||||
  final KeyVerification request;
 | 
			
		||||
 | 
			
		||||
  KeyVerificationDialog({
 | 
			
		||||
  const KeyVerificationDialog({
 | 
			
		||||
    Key key,
 | 
			
		||||
    this.request,
 | 
			
		||||
  });
 | 
			
		||||
  }) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  _KeyVerificationPageState createState() => _KeyVerificationPageState();
 | 
			
		||||
@ -76,6 +77,33 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
 | 
			
		||||
  Profile profile;
 | 
			
		||||
 | 
			
		||||
  Future<void> checkInput(String input) async {
 | 
			
		||||
    if (input == null || input.isEmpty) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    final valid = await showFutureLoadingDialog(
 | 
			
		||||
        context: context,
 | 
			
		||||
        future: () async {
 | 
			
		||||
          // make sure the loading spinner shows before we test the keys
 | 
			
		||||
          await Future.delayed(const Duration(milliseconds: 100));
 | 
			
		||||
          var valid = false;
 | 
			
		||||
          try {
 | 
			
		||||
            await widget.request.openSSSS(keyOrPassphrase: input);
 | 
			
		||||
            valid = true;
 | 
			
		||||
          } catch (_) {
 | 
			
		||||
            valid = false;
 | 
			
		||||
          }
 | 
			
		||||
          return valid;
 | 
			
		||||
        });
 | 
			
		||||
    if (valid.error != null) {
 | 
			
		||||
      await showOkAlertDialog(
 | 
			
		||||
        useRootNavigator: false,
 | 
			
		||||
        context: context,
 | 
			
		||||
        message: L10n.of(context).incorrectPassphraseOrKey,
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    User user;
 | 
			
		||||
@ -96,39 +124,13 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
        // prompt the user for their ssss passphrase / key
 | 
			
		||||
        final textEditingController = TextEditingController();
 | 
			
		||||
        String input;
 | 
			
		||||
        final checkInput = () async {
 | 
			
		||||
          if (input == null || input.isEmpty) {
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
          final valid = await showFutureLoadingDialog(
 | 
			
		||||
              context: context,
 | 
			
		||||
              future: () async {
 | 
			
		||||
                // make sure the loading spinner shows before we test the keys
 | 
			
		||||
                await Future.delayed(Duration(milliseconds: 100));
 | 
			
		||||
                var valid = false;
 | 
			
		||||
                try {
 | 
			
		||||
                  await widget.request.openSSSS(keyOrPassphrase: input);
 | 
			
		||||
                  valid = true;
 | 
			
		||||
                } catch (_) {
 | 
			
		||||
                  valid = false;
 | 
			
		||||
                }
 | 
			
		||||
                return valid;
 | 
			
		||||
              });
 | 
			
		||||
          if (valid.error != null) {
 | 
			
		||||
            await showOkAlertDialog(
 | 
			
		||||
              useRootNavigator: false,
 | 
			
		||||
              context: context,
 | 
			
		||||
              message: L10n.of(context).incorrectPassphraseOrKey,
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
        };
 | 
			
		||||
        body = Container(
 | 
			
		||||
          margin: EdgeInsets.only(left: 8.0, right: 8.0),
 | 
			
		||||
          margin: const EdgeInsets.only(left: 8.0, right: 8.0),
 | 
			
		||||
          child: Column(
 | 
			
		||||
            mainAxisSize: MainAxisSize.min,
 | 
			
		||||
            children: <Widget>[
 | 
			
		||||
              Text(L10n.of(context).askSSSSSign,
 | 
			
		||||
                  style: TextStyle(fontSize: 20)),
 | 
			
		||||
                  style: const TextStyle(fontSize: 20)),
 | 
			
		||||
              Container(height: 10),
 | 
			
		||||
              TextField(
 | 
			
		||||
                controller: textEditingController,
 | 
			
		||||
@ -136,7 +138,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
                autocorrect: false,
 | 
			
		||||
                onSubmitted: (s) {
 | 
			
		||||
                  input = s;
 | 
			
		||||
                  checkInput();
 | 
			
		||||
                  checkInput(input);
 | 
			
		||||
                },
 | 
			
		||||
                minLines: 1,
 | 
			
		||||
                maxLines: 1,
 | 
			
		||||
@ -145,7 +147,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
                  hintText: L10n.of(context).passphraseOrKey,
 | 
			
		||||
                  prefixStyle: TextStyle(color: Theme.of(context).primaryColor),
 | 
			
		||||
                  suffixStyle: TextStyle(color: Theme.of(context).primaryColor),
 | 
			
		||||
                  border: OutlineInputBorder(),
 | 
			
		||||
                  border: const OutlineInputBorder(),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
@ -155,7 +157,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
          label: L10n.of(context).submit,
 | 
			
		||||
          onPressed: () {
 | 
			
		||||
            input = textEditingController.text;
 | 
			
		||||
            checkInput();
 | 
			
		||||
            checkInput(input);
 | 
			
		||||
          },
 | 
			
		||||
        ));
 | 
			
		||||
        buttons.add(AdaptiveFlatButton(
 | 
			
		||||
@ -171,7 +173,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
            Row(children: [
 | 
			
		||||
              if (!PlatformInfos.isCupertinoStyle)
 | 
			
		||||
                Avatar(user?.avatarUrl, displayName),
 | 
			
		||||
              SizedBox(width: 12),
 | 
			
		||||
              const SizedBox(width: 12),
 | 
			
		||||
              Expanded(
 | 
			
		||||
                child: Column(
 | 
			
		||||
                  mainAxisSize: MainAxisSize.min,
 | 
			
		||||
@ -181,11 +183,11 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
                  children: [
 | 
			
		||||
                    Text(
 | 
			
		||||
                      displayName,
 | 
			
		||||
                      style: TextStyle(fontSize: 16),
 | 
			
		||||
                      style: const TextStyle(fontSize: 16),
 | 
			
		||||
                    ),
 | 
			
		||||
                    Text(
 | 
			
		||||
                      '${widget.request.userId} - ${widget.request.deviceId}',
 | 
			
		||||
                      style: TextStyle(
 | 
			
		||||
                      style: const TextStyle(
 | 
			
		||||
                        fontWeight: FontWeight.w300,
 | 
			
		||||
                        fontSize: 14,
 | 
			
		||||
                      ),
 | 
			
		||||
@ -222,7 +224,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
          children: <Widget>[
 | 
			
		||||
            Image.asset('assets/verification.png', fit: BoxFit.contain),
 | 
			
		||||
            const SizedBox(height: 16),
 | 
			
		||||
            CircularProgressIndicator.adaptive(strokeWidth: 2),
 | 
			
		||||
            const CircularProgressIndicator.adaptive(strokeWidth: 2),
 | 
			
		||||
            const SizedBox(height: 16),
 | 
			
		||||
            Text(
 | 
			
		||||
              L10n.of(context).waitingPartnerAcceptRequest,
 | 
			
		||||
@ -269,7 +271,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
          final numbers = widget.request.sasNumbers;
 | 
			
		||||
          final numbstr = '${numbers[0]}-${numbers[1]}-${numbers[2]}';
 | 
			
		||||
          compareWidget =
 | 
			
		||||
              TextSpan(text: numbstr, style: TextStyle(fontSize: 40));
 | 
			
		||||
              TextSpan(text: numbstr, style: const TextStyle(fontSize: 40));
 | 
			
		||||
        }
 | 
			
		||||
        body = Column(
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
@ -277,11 +279,11 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
            Center(
 | 
			
		||||
              child: Text(
 | 
			
		||||
                compareText,
 | 
			
		||||
                style: TextStyle(fontSize: 16),
 | 
			
		||||
                style: const TextStyle(fontSize: 16),
 | 
			
		||||
                textAlign: TextAlign.center,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            SizedBox(height: 10),
 | 
			
		||||
            const SizedBox(height: 10),
 | 
			
		||||
            Text.rich(
 | 
			
		||||
              compareWidget,
 | 
			
		||||
              textAlign: TextAlign.center,
 | 
			
		||||
@ -305,8 +307,8 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
        body = Column(
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
          children: <Widget>[
 | 
			
		||||
            CircularProgressIndicator.adaptive(strokeWidth: 2),
 | 
			
		||||
            SizedBox(height: 10),
 | 
			
		||||
            const CircularProgressIndicator.adaptive(strokeWidth: 2),
 | 
			
		||||
            const SizedBox(height: 10),
 | 
			
		||||
            Text(
 | 
			
		||||
              acceptText,
 | 
			
		||||
              textAlign: TextAlign.center,
 | 
			
		||||
@ -318,8 +320,9 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
        body = Column(
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
          children: <Widget>[
 | 
			
		||||
            Icon(Icons.check_circle_outlined, color: Colors.green, size: 200.0),
 | 
			
		||||
            SizedBox(height: 10),
 | 
			
		||||
            const Icon(Icons.check_circle_outlined,
 | 
			
		||||
                color: Colors.green, size: 200.0),
 | 
			
		||||
            const SizedBox(height: 10),
 | 
			
		||||
            Text(
 | 
			
		||||
              L10n.of(context).verifySuccess,
 | 
			
		||||
              textAlign: TextAlign.center,
 | 
			
		||||
@ -335,8 +338,8 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
        body = Column(
 | 
			
		||||
          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
          children: <Widget>[
 | 
			
		||||
            Icon(Icons.cancel, color: Colors.red, size: 200.0),
 | 
			
		||||
            SizedBox(height: 10),
 | 
			
		||||
            const Icon(Icons.cancel, color: Colors.red, size: 200.0),
 | 
			
		||||
            const SizedBox(height: 10),
 | 
			
		||||
            Text(
 | 
			
		||||
              'Error ${widget.request.canceledCode}: ${widget.request.canceledReason}',
 | 
			
		||||
              textAlign: TextAlign.center,
 | 
			
		||||
@ -355,7 +358,7 @@ class _KeyVerificationPageState extends State<KeyVerificationDialog> {
 | 
			
		||||
      child: Column(
 | 
			
		||||
        mainAxisSize: MainAxisSize.min,
 | 
			
		||||
        children: [
 | 
			
		||||
          SizedBox(height: 16),
 | 
			
		||||
          const SizedBox(height: 16),
 | 
			
		||||
          body,
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
@ -379,7 +382,7 @@ class _Emoji extends StatelessWidget {
 | 
			
		||||
  final KeyVerificationEmoji emoji;
 | 
			
		||||
  final List<dynamic> sasEmoji;
 | 
			
		||||
 | 
			
		||||
  _Emoji(this.emoji, this.sasEmoji);
 | 
			
		||||
  const _Emoji(this.emoji, this.sasEmoji);
 | 
			
		||||
 | 
			
		||||
  String getLocalizedName() {
 | 
			
		||||
    if (sasEmoji == null) {
 | 
			
		||||
@ -410,9 +413,9 @@ class _Emoji extends StatelessWidget {
 | 
			
		||||
    return Column(
 | 
			
		||||
      mainAxisSize: MainAxisSize.min,
 | 
			
		||||
      children: <Widget>[
 | 
			
		||||
        Text(emoji.emoji, style: TextStyle(fontSize: 50)),
 | 
			
		||||
        Text(emoji.emoji, style: const TextStyle(fontSize: 50)),
 | 
			
		||||
        Text(getLocalizedName()),
 | 
			
		||||
        Container(height: 10, width: 5),
 | 
			
		||||
        const SizedBox(height: 10, width: 5),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,8 @@ import '../config/setting_keys.dart';
 | 
			
		||||
import 'views/login_view.dart';
 | 
			
		||||
 | 
			
		||||
class Login extends StatefulWidget {
 | 
			
		||||
  const Login({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  LoginController createState() => LoginController();
 | 
			
		||||
}
 | 
			
		||||
@ -89,7 +91,7 @@ class LoginController extends State<Login> {
 | 
			
		||||
  void checkWellKnownWithCoolDown(String userId) async {
 | 
			
		||||
    _coolDown?.cancel();
 | 
			
		||||
    _coolDown = Timer(
 | 
			
		||||
      Duration(seconds: 1),
 | 
			
		||||
      const Duration(seconds: 1),
 | 
			
		||||
      () => _checkWellKnown(userId),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
@ -202,7 +204,7 @@ class LoginController extends State<Login> {
 | 
			
		||||
      okLabel: L10n.of(context).ok,
 | 
			
		||||
      cancelLabel: L10n.of(context).cancel,
 | 
			
		||||
      textFields: [
 | 
			
		||||
        DialogTextField(
 | 
			
		||||
        const DialogTextField(
 | 
			
		||||
          hintText: '******',
 | 
			
		||||
          obscureText: true,
 | 
			
		||||
          minLines: 1,
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,8 @@ import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:vrouter/vrouter.dart';
 | 
			
		||||
 | 
			
		||||
class NewGroup extends StatefulWidget {
 | 
			
		||||
  const NewGroup({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  NewGroupController createState() => NewGroupController();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,8 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
 | 
			
		||||
import 'package:permission_handler/permission_handler.dart';
 | 
			
		||||
 | 
			
		||||
class NewPrivateChat extends StatefulWidget {
 | 
			
		||||
  const NewPrivateChat({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  NewPrivateChatController createState() => NewPrivateChatController();
 | 
			
		||||
}
 | 
			
		||||
@ -54,7 +56,7 @@ class NewPrivateChatController extends State<NewPrivateChat> {
 | 
			
		||||
      context: context,
 | 
			
		||||
      useRootNavigator: false,
 | 
			
		||||
      //useSafeArea: false,
 | 
			
		||||
      builder: (_) => QrScannerModal(),
 | 
			
		||||
      builder: (_) => const QrScannerModal(),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,8 @@ import 'package:matrix/matrix.dart';
 | 
			
		||||
import 'package:vrouter/vrouter.dart';
 | 
			
		||||
 | 
			
		||||
class NewSpace extends StatefulWidget {
 | 
			
		||||
  const NewSpace({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  NewSpaceController createState() => NewSpaceController();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -63,7 +63,7 @@ class _PermissionSliderDialogState extends State<PermissionSliderDialog> {
 | 
			
		||||
                : _permission >= 50
 | 
			
		||||
                    ? '$_permission (${L10n.of(context).moderator})'
 | 
			
		||||
                    : _permission.toString())),
 | 
			
		||||
        Container(
 | 
			
		||||
        SizedBox(
 | 
			
		||||
          height: 56,
 | 
			
		||||
          child: slider,
 | 
			
		||||
        ),
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ class _QrScannerModalState extends State<QrScannerModal> {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading: IconButton(
 | 
			
		||||
          icon: Icon(Icons.close_outlined),
 | 
			
		||||
          icon: const Icon(Icons.close_outlined),
 | 
			
		||||
          onPressed: Navigator.of(context).pop,
 | 
			
		||||
          tooltip: L10n.of(context).close,
 | 
			
		||||
        ),
 | 
			
		||||
 | 
			
		||||
@ -45,10 +45,10 @@ class _RecordingDialogState extends State<RecordingDialog> {
 | 
			
		||||
      setState(() => _duration = Duration.zero);
 | 
			
		||||
      _recorderSubscription?.cancel();
 | 
			
		||||
      _recorderSubscription =
 | 
			
		||||
          Timer.periodic(Duration(milliseconds: 100), (_) async {
 | 
			
		||||
          Timer.periodic(const Duration(milliseconds: 100), (_) async {
 | 
			
		||||
        _amplitude = await _audioRecorder.getAmplitude();
 | 
			
		||||
        setState(() {
 | 
			
		||||
          _duration += Duration(milliseconds: 100);
 | 
			
		||||
          _duration += const Duration(milliseconds: 100);
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    } catch (e, s) {
 | 
			
		||||
@ -92,7 +92,7 @@ class _RecordingDialogState extends State<RecordingDialog> {
 | 
			
		||||
                height: maxDecibalWidth,
 | 
			
		||||
                alignment: Alignment.center,
 | 
			
		||||
                child: AnimatedContainer(
 | 
			
		||||
                  duration: Duration(milliseconds: 100),
 | 
			
		||||
                  duration: const Duration(milliseconds: 100),
 | 
			
		||||
                  width: decibalWidth,
 | 
			
		||||
                  height: decibalWidth,
 | 
			
		||||
                  decoration: BoxDecoration(
 | 
			
		||||
@ -101,11 +101,11 @@ class _RecordingDialogState extends State<RecordingDialog> {
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              SizedBox(width: 8),
 | 
			
		||||
              const SizedBox(width: 8),
 | 
			
		||||
              Expanded(
 | 
			
		||||
                child: Text(
 | 
			
		||||
                  '${L10n.of(context).recording}: $time',
 | 
			
		||||
                  style: TextStyle(
 | 
			
		||||
                  style: const TextStyle(
 | 
			
		||||
                    fontSize: 18,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
@ -163,8 +163,8 @@ class _RecordingDialogState extends State<RecordingDialog> {
 | 
			
		||||
              mainAxisSize: MainAxisSize.min,
 | 
			
		||||
              children: <Widget>[
 | 
			
		||||
                Text(L10n.of(context).send.toUpperCase()),
 | 
			
		||||
                SizedBox(width: 4),
 | 
			
		||||
                Icon(Icons.send_outlined, size: 15),
 | 
			
		||||
                const SizedBox(width: 4),
 | 
			
		||||
                const Icon(Icons.send_outlined, size: 15),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@ class SearchController extends State<Search> {
 | 
			
		||||
    setState(() => null);
 | 
			
		||||
    _coolDown?.cancel();
 | 
			
		||||
    _coolDown = Timer(
 | 
			
		||||
      Duration(milliseconds: 500),
 | 
			
		||||
      const Duration(milliseconds: 500),
 | 
			
		||||
      () => setState(() {
 | 
			
		||||
        genericSearchTerm = query;
 | 
			
		||||
        publicRoomsResponse = null;
 | 
			
		||||
 | 
			
		||||
@ -57,12 +57,12 @@ class _SendLocationDialogState extends State<SendLocationDialog> {
 | 
			
		||||
      try {
 | 
			
		||||
        _position = await Geolocator.getCurrentPosition(
 | 
			
		||||
          desiredAccuracy: LocationAccuracy.best,
 | 
			
		||||
          timeLimit: Duration(seconds: 30),
 | 
			
		||||
          timeLimit: const Duration(seconds: 30),
 | 
			
		||||
        );
 | 
			
		||||
      } on TimeoutException {
 | 
			
		||||
        _position = await Geolocator.getCurrentPosition(
 | 
			
		||||
          desiredAccuracy: LocationAccuracy.medium,
 | 
			
		||||
          timeLimit: Duration(seconds: 30),
 | 
			
		||||
          timeLimit: const Duration(seconds: 30),
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
      setState(() => position = _position);
 | 
			
		||||
@ -104,8 +104,8 @@ class _SendLocationDialogState extends State<SendLocationDialog> {
 | 
			
		||||
        mainAxisSize: MainAxisSize.min,
 | 
			
		||||
        mainAxisAlignment: MainAxisAlignment.center,
 | 
			
		||||
        children: [
 | 
			
		||||
          CupertinoActivityIndicator(),
 | 
			
		||||
          SizedBox(width: 12),
 | 
			
		||||
          const CupertinoActivityIndicator(),
 | 
			
		||||
          const SizedBox(width: 12),
 | 
			
		||||
          Text(L10n.of(context).obtainingLocation),
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,8 @@ import 'package:future_loading_dialog/future_loading_dialog.dart';
 | 
			
		||||
import '../widgets/matrix.dart';
 | 
			
		||||
 | 
			
		||||
class Settings extends StatefulWidget {
 | 
			
		||||
  const Settings({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  SettingsController createState() => SettingsController();
 | 
			
		||||
}
 | 
			
		||||
@ -117,7 +119,7 @@ class SettingsController extends State<Settings> {
 | 
			
		||||
          context: context,
 | 
			
		||||
          future: () async {
 | 
			
		||||
            // make sure the loading spinner shows before we test the keys
 | 
			
		||||
            await Future.delayed(Duration(milliseconds: 100));
 | 
			
		||||
            await Future.delayed(const Duration(milliseconds: 100));
 | 
			
		||||
            var valid = false;
 | 
			
		||||
            try {
 | 
			
		||||
              await handle.unlock(recoveryKey: input.single);
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,8 @@ import 'views/settings_3pid_view.dart';
 | 
			
		||||
class Settings3Pid extends StatefulWidget {
 | 
			
		||||
  static int sendAttempt = 0;
 | 
			
		||||
 | 
			
		||||
  const Settings3Pid({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Settings3PidController createState() => Settings3PidController();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -124,7 +124,7 @@ class SettingsAccountController extends State<SettingsAccount> {
 | 
			
		||||
      okLabel: L10n.of(context).ok,
 | 
			
		||||
      cancelLabel: L10n.of(context).cancel,
 | 
			
		||||
      textFields: [
 | 
			
		||||
        DialogTextField(
 | 
			
		||||
        const DialogTextField(
 | 
			
		||||
          obscureText: true,
 | 
			
		||||
          hintText: '******',
 | 
			
		||||
          minLines: 1,
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ import '../widgets/matrix.dart';
 | 
			
		||||
import '../utils/resize_image.dart';
 | 
			
		||||
 | 
			
		||||
class EmotesSettings extends StatefulWidget {
 | 
			
		||||
  EmotesSettings({Key key}) : super(key: key);
 | 
			
		||||
  const EmotesSettings({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  EmotesSettingsController createState() => EmotesSettingsController();
 | 
			
		||||
@ -80,13 +80,13 @@ class EmotesSettingsController extends State<EmotesSettings> {
 | 
			
		||||
    final content = client.accountData['im.ponies.emote_rooms']?.content ??
 | 
			
		||||
        <String, dynamic>{};
 | 
			
		||||
    if (active) {
 | 
			
		||||
      if (!(content['rooms'] is Map)) {
 | 
			
		||||
      if (content['rooms'] is! Map) {
 | 
			
		||||
        content['rooms'] = <String, dynamic>{};
 | 
			
		||||
      }
 | 
			
		||||
      if (!(content['rooms'][room.id] is Map)) {
 | 
			
		||||
      if (content['rooms'][room.id] is! Map) {
 | 
			
		||||
        content['rooms'][room.id] = <String, dynamic>{};
 | 
			
		||||
      }
 | 
			
		||||
      if (!(content['rooms'][room.id][stateKey ?? ''] is Map)) {
 | 
			
		||||
      if (content['rooms'][room.id][stateKey ?? ''] is! Map) {
 | 
			
		||||
        content['rooms'][room.id][stateKey ?? ''] = <String, dynamic>{};
 | 
			
		||||
      }
 | 
			
		||||
    } else if (content['rooms'] is Map && content['rooms'][room.id] is Map) {
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@ import '../widgets/matrix.dart';
 | 
			
		||||
class SettingsIgnoreList extends StatefulWidget {
 | 
			
		||||
  final String initialUserId;
 | 
			
		||||
 | 
			
		||||
  SettingsIgnoreList({Key key, this.initialUserId}) : super(key: key);
 | 
			
		||||
  const SettingsIgnoreList({Key key, this.initialUserId}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  SettingsIgnoreListController createState() => SettingsIgnoreListController();
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ import 'package:vrouter/vrouter.dart';
 | 
			
		||||
import 'views/settings_multiple_emotes_view.dart';
 | 
			
		||||
 | 
			
		||||
class MultipleEmotesSettings extends StatefulWidget {
 | 
			
		||||
  MultipleEmotesSettings({Key key}) : super(key: key);
 | 
			
		||||
  const MultipleEmotesSettings({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  MultipleEmotesSettingsController createState() =>
 | 
			
		||||
 | 
			
		||||
@ -49,6 +49,8 @@ class NotificationSettingsItem {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class SettingsNotifications extends StatefulWidget {
 | 
			
		||||
  const SettingsNotifications({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  SettingsNotificationsController createState() =>
 | 
			
		||||
      SettingsNotificationsController();
 | 
			
		||||
@ -57,7 +59,7 @@ class SettingsNotifications extends StatefulWidget {
 | 
			
		||||
class SettingsNotificationsController extends State<SettingsNotifications> {
 | 
			
		||||
  void openAndroidNotificationSettingsAction() async {
 | 
			
		||||
    await NotificationSetting.configureChannel(
 | 
			
		||||
      NotificationDetails(
 | 
			
		||||
      const NotificationDetails(
 | 
			
		||||
        android: AndroidNotificationDetails(
 | 
			
		||||
          AppConfig.pushNotificationsChannelId,
 | 
			
		||||
          AppConfig.pushNotificationsChannelName,
 | 
			
		||||
 | 
			
		||||
@ -55,7 +55,7 @@ class SettingsSecurityController extends State<SettingsSecurity> {
 | 
			
		||||
 | 
			
		||||
  void setAppLockAction() async {
 | 
			
		||||
    final currentLock =
 | 
			
		||||
        await FlutterSecureStorage().read(key: SettingKeys.appLockKey);
 | 
			
		||||
        await const FlutterSecureStorage().read(key: SettingKeys.appLockKey);
 | 
			
		||||
    if (currentLock?.isNotEmpty ?? false) {
 | 
			
		||||
      await AppLock.of(context).showLockScreen();
 | 
			
		||||
    }
 | 
			
		||||
@ -81,7 +81,7 @@ class SettingsSecurityController extends State<SettingsSecurity> {
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
    if (newLock != null) {
 | 
			
		||||
      await FlutterSecureStorage()
 | 
			
		||||
      await const FlutterSecureStorage()
 | 
			
		||||
          .write(key: SettingKeys.appLockKey, value: newLock.single);
 | 
			
		||||
      if (newLock.single.isEmpty) {
 | 
			
		||||
        AppLock.of(context).disable();
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,8 @@ import 'views/settings_style_view.dart';
 | 
			
		||||
import '../widgets/matrix.dart';
 | 
			
		||||
 | 
			
		||||
class SettingsStyle extends StatefulWidget {
 | 
			
		||||
  const SettingsStyle({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  SettingsStyleController createState() => SettingsStyleController();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,7 @@ class StickerPickerDialogState extends State<StickerPickerDialog> {
 | 
			
		||||
    final stickerPacks = widget.room.getImagePacks(ImagePackUsage.sticker);
 | 
			
		||||
    final packSlugs = stickerPacks.keys.toList();
 | 
			
		||||
 | 
			
		||||
    // ignore: prefer_function_declarations_over_variables
 | 
			
		||||
    final _packBuilder = (BuildContext context, int packIndex) {
 | 
			
		||||
      final pack = stickerPacks[packSlugs[packIndex]];
 | 
			
		||||
      final filteredImagePackImageEntried = pack.images.entries.toList();
 | 
			
		||||
@ -43,7 +44,7 @@ class StickerPickerDialogState extends State<StickerPickerDialog> {
 | 
			
		||||
      final packName = pack.pack.displayName ?? packSlugs[packIndex];
 | 
			
		||||
      return Column(
 | 
			
		||||
        children: <Widget>[
 | 
			
		||||
          if (packIndex != 0) SizedBox(height: 20),
 | 
			
		||||
          if (packIndex != 0) const SizedBox(height: 20),
 | 
			
		||||
          if (packName != 'user')
 | 
			
		||||
            ListTile(
 | 
			
		||||
              leading: Avatar(
 | 
			
		||||
@ -53,13 +54,13 @@ class StickerPickerDialogState extends State<StickerPickerDialog> {
 | 
			
		||||
              ),
 | 
			
		||||
              title: Text(packName),
 | 
			
		||||
            ),
 | 
			
		||||
          SizedBox(height: 6),
 | 
			
		||||
          const SizedBox(height: 6),
 | 
			
		||||
          GridView.builder(
 | 
			
		||||
            itemCount: imageKeys.length,
 | 
			
		||||
            gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
 | 
			
		||||
            gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
 | 
			
		||||
                maxCrossAxisExtent: 100),
 | 
			
		||||
            shrinkWrap: true,
 | 
			
		||||
            physics: NeverScrollableScrollPhysics(),
 | 
			
		||||
            physics: const NeverScrollableScrollPhysics(),
 | 
			
		||||
            itemBuilder: (BuildContext context, int imageIndex) {
 | 
			
		||||
              final image = pack.images[imageKeys[imageIndex]];
 | 
			
		||||
              final fakeEvent = Event.fromJson(<String, dynamic>{
 | 
			
		||||
@ -99,7 +100,7 @@ class StickerPickerDialogState extends State<StickerPickerDialog> {
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      body: Container(
 | 
			
		||||
      body: SizedBox(
 | 
			
		||||
        width: double.maxFinite,
 | 
			
		||||
        child: CustomScrollView(
 | 
			
		||||
          slivers: <Widget>[
 | 
			
		||||
@ -110,13 +111,13 @@ class StickerPickerDialogState extends State<StickerPickerDialog> {
 | 
			
		||||
              titleSpacing: 0,
 | 
			
		||||
              backgroundColor: Theme.of(context).dialogBackgroundColor,
 | 
			
		||||
              leading: IconButton(
 | 
			
		||||
                icon: Icon(Icons.close),
 | 
			
		||||
                icon: const Icon(Icons.close),
 | 
			
		||||
                onPressed: Navigator.of(context, rootNavigator: false).pop,
 | 
			
		||||
              ),
 | 
			
		||||
              title: DefaultAppBarSearchField(
 | 
			
		||||
                autofocus: false,
 | 
			
		||||
                hintText: L10n.of(context).search,
 | 
			
		||||
                suffix: Icon(Icons.search_outlined),
 | 
			
		||||
                suffix: const Icon(Icons.search_outlined),
 | 
			
		||||
                onChanged: (s) => setState(() => searchFilter = s),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,7 @@ class UserBottomSheet extends StatefulWidget {
 | 
			
		||||
 | 
			
		||||
class UserBottomSheetController extends State<UserBottomSheet> {
 | 
			
		||||
  void participantAction(String action) async {
 | 
			
		||||
    // ignore: prefer_function_declarations_over_variables
 | 
			
		||||
    final Function _askConfirmation =
 | 
			
		||||
        () async => (await showOkCancelAlertDialog(
 | 
			
		||||
              useRootNavigator: false,
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@ class ArchiveView extends StatelessWidget {
 | 
			
		||||
      future: controller.getArchive(context),
 | 
			
		||||
      builder: (BuildContext context, snapshot) => Scaffold(
 | 
			
		||||
        appBar: AppBar(
 | 
			
		||||
          leading: BackButton(),
 | 
			
		||||
          leading: const BackButton(),
 | 
			
		||||
          title: Text(L10n.of(context).archive),
 | 
			
		||||
          actions: [
 | 
			
		||||
            if (snapshot.hasData &&
 | 
			
		||||
@ -37,12 +37,12 @@ class ArchiveView extends StatelessWidget {
 | 
			
		||||
              ));
 | 
			
		||||
            }
 | 
			
		||||
            if (!snapshot.hasData) {
 | 
			
		||||
              return Center(
 | 
			
		||||
              return const Center(
 | 
			
		||||
                  child: CircularProgressIndicator.adaptive(strokeWidth: 2));
 | 
			
		||||
            } else {
 | 
			
		||||
              controller.archive = snapshot.data;
 | 
			
		||||
              if (controller.archive.isEmpty) {
 | 
			
		||||
                return Center(child: Icon(Icons.archive_outlined, size: 80));
 | 
			
		||||
                return const Center(child: Icon(Icons.archive_outlined, size: 80));
 | 
			
		||||
              }
 | 
			
		||||
              return ListView.builder(
 | 
			
		||||
                itemCount: controller.archive.length,
 | 
			
		||||
 | 
			
		||||
@ -51,7 +51,7 @@ class ChatDetailsView extends StatelessWidget {
 | 
			
		||||
                  (BuildContext context, bool innerBoxIsScrolled) => <Widget>[
 | 
			
		||||
                SliverAppBar(
 | 
			
		||||
                  leading: IconButton(
 | 
			
		||||
                    icon: Icon(Icons.close_outlined),
 | 
			
		||||
                    icon: const Icon(Icons.close_outlined),
 | 
			
		||||
                    onPressed: () =>
 | 
			
		||||
                        VRouter.of(context).path.startsWith('/spaces/')
 | 
			
		||||
                            ? VRouter.of(context).pop()
 | 
			
		||||
@ -66,7 +66,7 @@ class ChatDetailsView extends StatelessWidget {
 | 
			
		||||
                    if (room.canonicalAlias?.isNotEmpty ?? false)
 | 
			
		||||
                      IconButton(
 | 
			
		||||
                        tooltip: L10n.of(context).share,
 | 
			
		||||
                        icon: Icon(Icons.share_outlined),
 | 
			
		||||
                        icon: const Icon(Icons.share_outlined),
 | 
			
		||||
                        onPressed: () => FluffyShare.share(
 | 
			
		||||
                            AppConfig.inviteLinkPrefix + room.canonicalAlias,
 | 
			
		||||
                            context),
 | 
			
		||||
@ -107,7 +107,7 @@ class ChatDetailsView extends StatelessWidget {
 | 
			
		||||
                                          .scaffoldBackgroundColor,
 | 
			
		||||
                                      foregroundColor: Colors.grey,
 | 
			
		||||
                                      radius: Avatar.defaultSize / 2,
 | 
			
		||||
                                      child: Icon(Icons.edit_outlined),
 | 
			
		||||
                                      child: const Icon(Icons.edit_outlined),
 | 
			
		||||
                                    )
 | 
			
		||||
                                  : null,
 | 
			
		||||
                              title: Text(
 | 
			
		||||
@ -121,7 +121,7 @@ class ChatDetailsView extends StatelessWidget {
 | 
			
		||||
                                text: room.topic?.isEmpty ?? true
 | 
			
		||||
                                    ? L10n.of(context).addGroupDescription
 | 
			
		||||
                                    : room.topic,
 | 
			
		||||
                                linkStyle: TextStyle(color: Colors.blueAccent),
 | 
			
		||||
                                linkStyle: const TextStyle(color: Colors.blueAccent),
 | 
			
		||||
                                textStyle: TextStyle(
 | 
			
		||||
                                  fontSize: 14,
 | 
			
		||||
                                  color: Theme.of(context)
 | 
			
		||||
@ -136,7 +136,7 @@ class ChatDetailsView extends StatelessWidget {
 | 
			
		||||
                                  ? controller.setTopicAction
 | 
			
		||||
                                  : null,
 | 
			
		||||
                            ),
 | 
			
		||||
                            Divider(thickness: 1),
 | 
			
		||||
                            const Divider(thickness: 1),
 | 
			
		||||
                            ListTile(
 | 
			
		||||
                              title: Text(
 | 
			
		||||
                                L10n.of(context).settings,
 | 
			
		||||
@ -153,7 +153,7 @@ class ChatDetailsView extends StatelessWidget {
 | 
			
		||||
                                  backgroundColor:
 | 
			
		||||
                                      Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                                  foregroundColor: Colors.grey,
 | 
			
		||||
                                  child: Icon(Icons.people_outlined),
 | 
			
		||||
                                  child: const Icon(Icons.people_outlined),
 | 
			
		||||
                                ),
 | 
			
		||||
                                title: Text(
 | 
			
		||||
                                    L10n.of(context).changeTheNameOfTheGroup),
 | 
			
		||||
@ -167,7 +167,7 @@ class ChatDetailsView extends StatelessWidget {
 | 
			
		||||
                                  backgroundColor:
 | 
			
		||||
                                      Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                                  foregroundColor: Colors.grey,
 | 
			
		||||
                                  child: Icon(Icons.link_outlined),
 | 
			
		||||
                                  child: const Icon(Icons.link_outlined),
 | 
			
		||||
                                ),
 | 
			
		||||
                                onTap: controller.editAliases,
 | 
			
		||||
                                title: Text(L10n.of(context).editRoomAliases),
 | 
			
		||||
@ -181,7 +181,7 @@ class ChatDetailsView extends StatelessWidget {
 | 
			
		||||
                                backgroundColor:
 | 
			
		||||
                                    Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                                foregroundColor: Colors.grey,
 | 
			
		||||
                                child: Icon(Icons.insert_emoticon_outlined),
 | 
			
		||||
                                child: const Icon(Icons.insert_emoticon_outlined),
 | 
			
		||||
                              ),
 | 
			
		||||
                              title: Text(L10n.of(context).emoteSettings),
 | 
			
		||||
                              subtitle: Text(L10n.of(context).setCustomEmotes),
 | 
			
		||||
@ -211,7 +211,7 @@ class ChatDetailsView extends StatelessWidget {
 | 
			
		||||
                                    backgroundColor: Theme.of(context)
 | 
			
		||||
                                        .scaffoldBackgroundColor,
 | 
			
		||||
                                    foregroundColor: Colors.grey,
 | 
			
		||||
                                    child: Icon(Icons.public_outlined)),
 | 
			
		||||
                                    child: const Icon(Icons.public_outlined)),
 | 
			
		||||
                                title: Text(L10n.of(context)
 | 
			
		||||
                                    .whoIsAllowedToJoinThisGroup),
 | 
			
		||||
                                subtitle: Text(
 | 
			
		||||
@ -258,7 +258,7 @@ class ChatDetailsView extends StatelessWidget {
 | 
			
		||||
                                  backgroundColor:
 | 
			
		||||
                                      Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                                  foregroundColor: Colors.grey,
 | 
			
		||||
                                  child: Icon(Icons.visibility_outlined),
 | 
			
		||||
                                  child: const Icon(Icons.visibility_outlined),
 | 
			
		||||
                                ),
 | 
			
		||||
                                title: Text(L10n.of(context)
 | 
			
		||||
                                    .visibilityOfTheChatHistory),
 | 
			
		||||
@ -297,7 +297,7 @@ class ChatDetailsView extends StatelessWidget {
 | 
			
		||||
                                    backgroundColor: Theme.of(context)
 | 
			
		||||
                                        .scaffoldBackgroundColor,
 | 
			
		||||
                                    foregroundColor: Colors.grey,
 | 
			
		||||
                                    child: Icon(Icons.info_outline),
 | 
			
		||||
                                    child: const Icon(Icons.info_outline),
 | 
			
		||||
                                  ),
 | 
			
		||||
                                  title: Text(
 | 
			
		||||
                                      L10n.of(context).areGuestsAllowedToJoin),
 | 
			
		||||
@ -315,12 +315,12 @@ class ChatDetailsView extends StatelessWidget {
 | 
			
		||||
                                backgroundColor:
 | 
			
		||||
                                    Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                                foregroundColor: Colors.grey,
 | 
			
		||||
                                child: Icon(Icons.edit_attributes_outlined),
 | 
			
		||||
                                child: const Icon(Icons.edit_attributes_outlined),
 | 
			
		||||
                              ),
 | 
			
		||||
                              onTap: () =>
 | 
			
		||||
                                  VRouter.of(context).to('permissions'),
 | 
			
		||||
                            ),
 | 
			
		||||
                            Divider(thickness: 1),
 | 
			
		||||
                            const Divider(thickness: 1),
 | 
			
		||||
                            ListTile(
 | 
			
		||||
                              title: Text(
 | 
			
		||||
                                actualMembersCount > 1
 | 
			
		||||
@ -342,7 +342,7 @@ class ChatDetailsView extends StatelessWidget {
 | 
			
		||||
                                          Theme.of(context).primaryColor,
 | 
			
		||||
                                      foregroundColor: Colors.white,
 | 
			
		||||
                                      radius: Avatar.defaultSize / 2,
 | 
			
		||||
                                      child: Icon(Icons.add_outlined),
 | 
			
		||||
                                      child: const Icon(Icons.add_outlined),
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    onTap: () =>
 | 
			
		||||
                                        VRouter.of(context).to('invite'),
 | 
			
		||||
@ -361,7 +361,7 @@ class ChatDetailsView extends StatelessWidget {
 | 
			
		||||
                              leading: CircleAvatar(
 | 
			
		||||
                                backgroundColor:
 | 
			
		||||
                                    Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                                child: Icon(
 | 
			
		||||
                                child: const Icon(
 | 
			
		||||
                                  Icons.refresh,
 | 
			
		||||
                                  color: Colors.grey,
 | 
			
		||||
                                ),
 | 
			
		||||
 | 
			
		||||
@ -21,19 +21,19 @@ class ChatEncryptionSettingsView extends StatelessWidget {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading: IconButton(
 | 
			
		||||
          icon: Icon(Icons.close_outlined),
 | 
			
		||||
          icon: const Icon(Icons.close_outlined),
 | 
			
		||||
          onPressed: () =>
 | 
			
		||||
              VRouter.of(context).toSegments(['rooms', controller.roomId]),
 | 
			
		||||
        ),
 | 
			
		||||
        title: Text(L10n.of(context).tapOnDeviceToVerify),
 | 
			
		||||
        bottom: PreferredSize(
 | 
			
		||||
          preferredSize: Size.fromHeight(56),
 | 
			
		||||
          preferredSize: const Size.fromHeight(56),
 | 
			
		||||
          child: ListTile(
 | 
			
		||||
            title: Text(L10n.of(context).deviceVerifyDescription),
 | 
			
		||||
            leading: CircleAvatar(
 | 
			
		||||
              backgroundColor: Theme.of(context).secondaryHeaderColor,
 | 
			
		||||
              foregroundColor: Theme.of(context).colorScheme.secondary,
 | 
			
		||||
              child: Icon(Icons.lock),
 | 
			
		||||
              child: const Icon(Icons.lock),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
@ -54,14 +54,14 @@ class ChatEncryptionSettingsView extends StatelessWidget {
 | 
			
		||||
                    );
 | 
			
		||||
                  }
 | 
			
		||||
                  if (!snapshot.hasData) {
 | 
			
		||||
                    return Center(
 | 
			
		||||
                    return const Center(
 | 
			
		||||
                        child:
 | 
			
		||||
                            CircularProgressIndicator.adaptive(strokeWidth: 2));
 | 
			
		||||
                  }
 | 
			
		||||
                  final deviceKeys = snapshot.data;
 | 
			
		||||
                  return ListView.builder(
 | 
			
		||||
                    shrinkWrap: true,
 | 
			
		||||
                    physics: NeverScrollableScrollPhysics(),
 | 
			
		||||
                    physics: const NeverScrollableScrollPhysics(),
 | 
			
		||||
                    itemCount: deviceKeys.length,
 | 
			
		||||
                    itemBuilder: (BuildContext context, int i) => Column(
 | 
			
		||||
                      mainAxisSize: MainAxisSize.min,
 | 
			
		||||
@ -69,7 +69,7 @@ class ChatEncryptionSettingsView extends StatelessWidget {
 | 
			
		||||
                        if (i == 0 ||
 | 
			
		||||
                            deviceKeys[i].userId !=
 | 
			
		||||
                                deviceKeys[i - 1].userId) ...{
 | 
			
		||||
                          Divider(height: 1, thickness: 1),
 | 
			
		||||
                          const Divider(height: 1, thickness: 1),
 | 
			
		||||
                          PopupMenuButton(
 | 
			
		||||
                            onSelected: (action) => controller.onSelected(
 | 
			
		||||
                                context, action, deviceKeys[i]),
 | 
			
		||||
@ -103,7 +103,7 @@ class ChatEncryptionSettingsView extends StatelessWidget {
 | 
			
		||||
                              ),
 | 
			
		||||
                              subtitle: Text(
 | 
			
		||||
                                deviceKeys[i].userId,
 | 
			
		||||
                                style: TextStyle(fontWeight: FontWeight.w300),
 | 
			
		||||
                                style: const TextStyle(fontWeight: FontWeight.w300),
 | 
			
		||||
                              ),
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
@ -152,9 +152,9 @@ class ChatEncryptionSettingsView extends StatelessWidget {
 | 
			
		||||
                              children: [
 | 
			
		||||
                                Text(
 | 
			
		||||
                                  deviceKeys[i].deviceId,
 | 
			
		||||
                                  style: TextStyle(fontWeight: FontWeight.w300),
 | 
			
		||||
                                  style: const TextStyle(fontWeight: FontWeight.w300),
 | 
			
		||||
                                ),
 | 
			
		||||
                                Spacer(),
 | 
			
		||||
                                const Spacer(),
 | 
			
		||||
                                Text(
 | 
			
		||||
                                  deviceKeys[i].blocked
 | 
			
		||||
                                      ? L10n.of(context).blocked
 | 
			
		||||
 | 
			
		||||
@ -101,12 +101,12 @@ class ChatListView extends StatelessWidget {
 | 
			
		||||
                        ? null
 | 
			
		||||
                        : Builder(
 | 
			
		||||
                            builder: (context) => IconButton(
 | 
			
		||||
                                  icon: Icon(Icons.group_work_outlined),
 | 
			
		||||
                                  icon: const Icon(Icons.group_work_outlined),
 | 
			
		||||
                                  onPressed: Scaffold.of(context).openDrawer,
 | 
			
		||||
                                ))
 | 
			
		||||
                    : IconButton(
 | 
			
		||||
                        tooltip: L10n.of(context).cancel,
 | 
			
		||||
                        icon: Icon(Icons.close_outlined),
 | 
			
		||||
                        icon: const Icon(Icons.close_outlined),
 | 
			
		||||
                        onPressed: controller.cancelAction,
 | 
			
		||||
                        color: Theme.of(context).colorScheme.primary,
 | 
			
		||||
                      ),
 | 
			
		||||
@ -125,7 +125,7 @@ class ChatListView extends StatelessWidget {
 | 
			
		||||
                                    .isDirectChat)
 | 
			
		||||
                              IconButton(
 | 
			
		||||
                                tooltip: L10n.of(context).addToSpace,
 | 
			
		||||
                                icon: Icon(Icons.group_work_outlined),
 | 
			
		||||
                                icon: const Icon(Icons.group_work_outlined),
 | 
			
		||||
                                onPressed: controller.addOrRemoveToSpace,
 | 
			
		||||
                              ),
 | 
			
		||||
                            if (controller.selectedRoomIds.length == 1)
 | 
			
		||||
@ -143,7 +143,7 @@ class ChatListView extends StatelessWidget {
 | 
			
		||||
                            if (controller.selectedRoomIds.length == 1)
 | 
			
		||||
                              IconButton(
 | 
			
		||||
                                tooltip: L10n.of(context).toggleFavorite,
 | 
			
		||||
                                icon: Icon(Icons.push_pin_outlined),
 | 
			
		||||
                                icon: const Icon(Icons.push_pin_outlined),
 | 
			
		||||
                                onPressed: controller.toggleFavouriteRoom,
 | 
			
		||||
                              ),
 | 
			
		||||
                            if (controller.selectedRoomIds.length == 1)
 | 
			
		||||
@ -160,14 +160,14 @@ class ChatListView extends StatelessWidget {
 | 
			
		||||
                                onPressed: controller.toggleMuted,
 | 
			
		||||
                              ),
 | 
			
		||||
                            IconButton(
 | 
			
		||||
                              icon: Icon(Icons.delete_outlined),
 | 
			
		||||
                              icon: const Icon(Icons.delete_outlined),
 | 
			
		||||
                              tooltip: L10n.of(context).archive,
 | 
			
		||||
                              onPressed: controller.archiveAction,
 | 
			
		||||
                            ),
 | 
			
		||||
                          ]
 | 
			
		||||
                        : [
 | 
			
		||||
                            IconButton(
 | 
			
		||||
                              icon: Icon(Icons.search_outlined),
 | 
			
		||||
                              icon: const Icon(Icons.search_outlined),
 | 
			
		||||
                              tooltip: L10n.of(context).search,
 | 
			
		||||
                              onPressed: () =>
 | 
			
		||||
                                  VRouter.of(context).to('/search'),
 | 
			
		||||
@ -180,8 +180,8 @@ class ChatListView extends StatelessWidget {
 | 
			
		||||
                                  child: Row(
 | 
			
		||||
                                    mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                                    children: [
 | 
			
		||||
                                      Icon(Icons.edit_outlined),
 | 
			
		||||
                                      SizedBox(width: 12),
 | 
			
		||||
                                      const Icon(Icons.edit_outlined),
 | 
			
		||||
                                      const SizedBox(width: 12),
 | 
			
		||||
                                      Text(L10n.of(context).setStatus),
 | 
			
		||||
                                    ],
 | 
			
		||||
                                  ),
 | 
			
		||||
@ -191,8 +191,8 @@ class ChatListView extends StatelessWidget {
 | 
			
		||||
                                  child: Row(
 | 
			
		||||
                                    mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                                    children: [
 | 
			
		||||
                                      Icon(Icons.group_add_outlined),
 | 
			
		||||
                                      SizedBox(width: 12),
 | 
			
		||||
                                      const Icon(Icons.group_add_outlined),
 | 
			
		||||
                                      const SizedBox(width: 12),
 | 
			
		||||
                                      Text(L10n.of(context).createNewGroup),
 | 
			
		||||
                                    ],
 | 
			
		||||
                                  ),
 | 
			
		||||
@ -202,8 +202,8 @@ class ChatListView extends StatelessWidget {
 | 
			
		||||
                                  child: Row(
 | 
			
		||||
                                    mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                                    children: [
 | 
			
		||||
                                      Icon(Icons.group_work_outlined),
 | 
			
		||||
                                      SizedBox(width: 12),
 | 
			
		||||
                                      const Icon(Icons.group_work_outlined),
 | 
			
		||||
                                      const SizedBox(width: 12),
 | 
			
		||||
                                      Text(L10n.of(context).createNewSpace),
 | 
			
		||||
                                    ],
 | 
			
		||||
                                  ),
 | 
			
		||||
@ -213,8 +213,8 @@ class ChatListView extends StatelessWidget {
 | 
			
		||||
                                  child: Row(
 | 
			
		||||
                                    mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                                    children: [
 | 
			
		||||
                                      Icon(Icons.share_outlined),
 | 
			
		||||
                                      SizedBox(width: 12),
 | 
			
		||||
                                      const Icon(Icons.share_outlined),
 | 
			
		||||
                                      const SizedBox(width: 12),
 | 
			
		||||
                                      Text(L10n.of(context).inviteContact),
 | 
			
		||||
                                    ],
 | 
			
		||||
                                  ),
 | 
			
		||||
@ -224,8 +224,8 @@ class ChatListView extends StatelessWidget {
 | 
			
		||||
                                  child: Row(
 | 
			
		||||
                                    mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                                    children: [
 | 
			
		||||
                                      Icon(Icons.archive_outlined),
 | 
			
		||||
                                      SizedBox(width: 12),
 | 
			
		||||
                                      const Icon(Icons.archive_outlined),
 | 
			
		||||
                                      const SizedBox(width: 12),
 | 
			
		||||
                                      Text(L10n.of(context).archive),
 | 
			
		||||
                                    ],
 | 
			
		||||
                                  ),
 | 
			
		||||
@ -235,8 +235,8 @@ class ChatListView extends StatelessWidget {
 | 
			
		||||
                                  child: Row(
 | 
			
		||||
                                    mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                                    children: [
 | 
			
		||||
                                      Icon(Icons.settings_outlined),
 | 
			
		||||
                                      SizedBox(width: 12),
 | 
			
		||||
                                      const Icon(Icons.settings_outlined),
 | 
			
		||||
                                      const SizedBox(width: 12),
 | 
			
		||||
                                      Text(L10n.of(context).settings),
 | 
			
		||||
                                    ],
 | 
			
		||||
                                  ),
 | 
			
		||||
@ -256,7 +256,7 @@ class ChatListView extends StatelessWidget {
 | 
			
		||||
                                .displayname),
 | 
			
		||||
              ),
 | 
			
		||||
              body: Column(children: [
 | 
			
		||||
                ConnectionStatusHeader(),
 | 
			
		||||
                const ConnectionStatusHeader(),
 | 
			
		||||
                Expanded(child: _ChatListViewBody(controller)),
 | 
			
		||||
              ]),
 | 
			
		||||
              floatingActionButton: selectMode == SelectMode.normal
 | 
			
		||||
@ -265,14 +265,14 @@ class ChatListView extends StatelessWidget {
 | 
			
		||||
                          heroTag: 'main_fab',
 | 
			
		||||
                          onPressed: () =>
 | 
			
		||||
                              VRouter.of(context).to('/newprivatechat'),
 | 
			
		||||
                          icon: Icon(CupertinoIcons.chat_bubble),
 | 
			
		||||
                          icon: const Icon(CupertinoIcons.chat_bubble),
 | 
			
		||||
                          label: Text(L10n.of(context).newChat),
 | 
			
		||||
                        )
 | 
			
		||||
                      : FloatingActionButton(
 | 
			
		||||
                          heroTag: 'main_fab',
 | 
			
		||||
                          onPressed: () =>
 | 
			
		||||
                              VRouter.of(context).to('/newprivatechat'),
 | 
			
		||||
                          child: Icon(CupertinoIcons.chat_bubble),
 | 
			
		||||
                          child: const Icon(CupertinoIcons.chat_bubble),
 | 
			
		||||
                        )
 | 
			
		||||
                  : null,
 | 
			
		||||
              bottomNavigationBar: Matrix.of(context).isMultiAccount
 | 
			
		||||
@ -291,7 +291,7 @@ class ChatListView extends StatelessWidget {
 | 
			
		||||
                        child: Column(
 | 
			
		||||
                          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                          children: [
 | 
			
		||||
                            Divider(height: 1),
 | 
			
		||||
                            const Divider(height: 1),
 | 
			
		||||
                            Builder(builder: (context) {
 | 
			
		||||
                              final items = getBottomBarItems(context);
 | 
			
		||||
                              if (items.length == 1) {
 | 
			
		||||
@ -378,7 +378,7 @@ class ChatListView extends StatelessWidget {
 | 
			
		||||
                                  backgroundColor:
 | 
			
		||||
                                      Theme.of(context).primaryColor,
 | 
			
		||||
                                  radius: Avatar.defaultSize / 2,
 | 
			
		||||
                                  child: Icon(Icons.home_outlined),
 | 
			
		||||
                                  child: const Icon(Icons.home_outlined),
 | 
			
		||||
                                ),
 | 
			
		||||
                                title: Text(L10n.of(context).allChats),
 | 
			
		||||
                                onTap: () =>
 | 
			
		||||
@ -399,7 +399,7 @@ class ChatListView extends StatelessWidget {
 | 
			
		||||
                              onTap: () => controller.setActiveSpaceId(
 | 
			
		||||
                                  context, space.id),
 | 
			
		||||
                              trailing: IconButton(
 | 
			
		||||
                                icon: Icon(Icons.edit_outlined),
 | 
			
		||||
                                icon: const Icon(Icons.edit_outlined),
 | 
			
		||||
                                onPressed: () =>
 | 
			
		||||
                                    controller.editSpace(context, space.id),
 | 
			
		||||
                              ),
 | 
			
		||||
@ -425,7 +425,7 @@ class _ChatListViewBody extends StatelessWidget {
 | 
			
		||||
          .onSync
 | 
			
		||||
          .stream
 | 
			
		||||
          .where((s) => s.hasRoomUpdate)
 | 
			
		||||
          .rateLimit(Duration(seconds: 1)),
 | 
			
		||||
          .rateLimit(const Duration(seconds: 1)),
 | 
			
		||||
      builder: (context, snapshot) {
 | 
			
		||||
        return FutureBuilder<void>(
 | 
			
		||||
          future: controller.waitForFirstSync(),
 | 
			
		||||
@ -441,7 +441,7 @@ class _ChatListViewBody extends StatelessWidget {
 | 
			
		||||
                  mainAxisAlignment: MainAxisAlignment.center,
 | 
			
		||||
                  mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                  children: <Widget>[
 | 
			
		||||
                    Icon(
 | 
			
		||||
                    const Icon(
 | 
			
		||||
                      Icons.maps_ugc_outlined,
 | 
			
		||||
                      size: 80,
 | 
			
		||||
                      color: Colors.grey,
 | 
			
		||||
@ -450,7 +450,7 @@ class _ChatListViewBody extends StatelessWidget {
 | 
			
		||||
                      child: Text(
 | 
			
		||||
                        L10n.of(context).startYourFirstChat,
 | 
			
		||||
                        textAlign: TextAlign.start,
 | 
			
		||||
                        style: TextStyle(
 | 
			
		||||
                        style: const TextStyle(
 | 
			
		||||
                          color: Colors.grey,
 | 
			
		||||
                          fontSize: 16,
 | 
			
		||||
                        ),
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ class ChatPermissionsSettingsView extends StatelessWidget {
 | 
			
		||||
        leading: VRouter.of(context).path.startsWith('/spaces/')
 | 
			
		||||
            ? null
 | 
			
		||||
            : IconButton(
 | 
			
		||||
                icon: Icon(Icons.close_outlined),
 | 
			
		||||
                icon: const Icon(Icons.close_outlined),
 | 
			
		||||
                onPressed: () => VRouter.of(context)
 | 
			
		||||
                    .toSegments(['rooms', controller.roomId]),
 | 
			
		||||
              ),
 | 
			
		||||
@ -37,10 +37,10 @@ class ChatPermissionsSettingsView extends StatelessWidget {
 | 
			
		||||
            final powerLevelsContent = Map<String, dynamic>.from(
 | 
			
		||||
                room.getState(EventTypes.RoomPowerLevels).content);
 | 
			
		||||
            final powerLevels = Map<String, dynamic>.from(powerLevelsContent)
 | 
			
		||||
              ..removeWhere((k, v) => !(v is int));
 | 
			
		||||
              ..removeWhere((k, v) => v is! int);
 | 
			
		||||
            final eventsPowerLevels =
 | 
			
		||||
                Map<String, dynamic>.from(powerLevelsContent['events'] ?? {})
 | 
			
		||||
                  ..removeWhere((k, v) => !(v is int));
 | 
			
		||||
                  ..removeWhere((k, v) => v is! int);
 | 
			
		||||
            return Column(
 | 
			
		||||
              children: [
 | 
			
		||||
                Column(
 | 
			
		||||
@ -53,7 +53,7 @@ class ChatPermissionsSettingsView extends StatelessWidget {
 | 
			
		||||
                        onTap: () => controller.editPowerLevel(
 | 
			
		||||
                            context, entry.key, entry.value),
 | 
			
		||||
                      ),
 | 
			
		||||
                    Divider(thickness: 1),
 | 
			
		||||
                    const Divider(thickness: 1),
 | 
			
		||||
                    ListTile(
 | 
			
		||||
                      title: Text(
 | 
			
		||||
                        L10n.of(context).notifications,
 | 
			
		||||
@ -64,7 +64,7 @@ class ChatPermissionsSettingsView extends StatelessWidget {
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                    Builder(builder: (context) {
 | 
			
		||||
                      final key = 'rooms';
 | 
			
		||||
                      const key = 'rooms';
 | 
			
		||||
                      final int value = powerLevelsContent
 | 
			
		||||
                              .containsKey('notifications')
 | 
			
		||||
                          ? powerLevelsContent['notifications']['rooms'] ?? 0
 | 
			
		||||
@ -78,7 +78,7 @@ class ChatPermissionsSettingsView extends StatelessWidget {
 | 
			
		||||
                            category: 'notifications'),
 | 
			
		||||
                      );
 | 
			
		||||
                    }),
 | 
			
		||||
                    Divider(thickness: 1),
 | 
			
		||||
                    const Divider(thickness: 1),
 | 
			
		||||
                    ListTile(
 | 
			
		||||
                      title: Text(
 | 
			
		||||
                        L10n.of(context).configureChat,
 | 
			
		||||
@ -99,12 +99,12 @@ class ChatPermissionsSettingsView extends StatelessWidget {
 | 
			
		||||
                              category: 'events'),
 | 
			
		||||
                        ),
 | 
			
		||||
                    if (room.canSendEvent(EventTypes.RoomTombstone)) ...{
 | 
			
		||||
                      Divider(thickness: 1),
 | 
			
		||||
                      const Divider(thickness: 1),
 | 
			
		||||
                      FutureBuilder<Capabilities>(
 | 
			
		||||
                        future: room.client.getCapabilities(),
 | 
			
		||||
                        builder: (context, snapshot) {
 | 
			
		||||
                          if (!snapshot.hasData) {
 | 
			
		||||
                            return Center(
 | 
			
		||||
                            return const Center(
 | 
			
		||||
                                child: CircularProgressIndicator.adaptive(
 | 
			
		||||
                                    strokeWidth: 2));
 | 
			
		||||
                          }
 | 
			
		||||
 | 
			
		||||
@ -66,7 +66,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
      },
 | 
			
		||||
      child: StreamBuilder(
 | 
			
		||||
        stream: controller.room.onUpdate.stream
 | 
			
		||||
            .rateLimit(Duration(milliseconds: 250)),
 | 
			
		||||
            .rateLimit(const Duration(milliseconds: 250)),
 | 
			
		||||
        builder: (context, snapshot) => Scaffold(
 | 
			
		||||
          appBar: AppBar(
 | 
			
		||||
            actionsIconTheme: IconThemeData(
 | 
			
		||||
@ -76,7 +76,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
            ),
 | 
			
		||||
            leading: controller.selectMode
 | 
			
		||||
                ? IconButton(
 | 
			
		||||
                    icon: Icon(Icons.close),
 | 
			
		||||
                    icon: const Icon(Icons.close),
 | 
			
		||||
                    onPressed: controller.clearSelectedEvents,
 | 
			
		||||
                    tooltip: L10n.of(context).close,
 | 
			
		||||
                    color: Theme.of(context).colorScheme.primary,
 | 
			
		||||
@ -117,7 +117,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                .where((p) =>
 | 
			
		||||
                                    p.senderId ==
 | 
			
		||||
                                    controller.room.directChatMatrixID)
 | 
			
		||||
                                .rateLimit(Duration(seconds: 1)),
 | 
			
		||||
                                .rateLimit(const Duration(seconds: 1)),
 | 
			
		||||
                            builder: (context, snapshot) => Text(
 | 
			
		||||
                                  controller.room.getLocalizedStatus(context),
 | 
			
		||||
                                  maxLines: 1,
 | 
			
		||||
@ -129,7 +129,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                  color:
 | 
			
		||||
                                      Theme.of(context).colorScheme.secondary,
 | 
			
		||||
                                  size: 13),
 | 
			
		||||
                              SizedBox(width: 4),
 | 
			
		||||
                              const SizedBox(width: 4),
 | 
			
		||||
                              Expanded(
 | 
			
		||||
                                child: Text(
 | 
			
		||||
                                  controller.room
 | 
			
		||||
@ -150,24 +150,24 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                ? <Widget>[
 | 
			
		||||
                    if (controller.canEditSelectedEvents)
 | 
			
		||||
                      IconButton(
 | 
			
		||||
                        icon: Icon(Icons.edit_outlined),
 | 
			
		||||
                        icon: const Icon(Icons.edit_outlined),
 | 
			
		||||
                        tooltip: L10n.of(context).edit,
 | 
			
		||||
                        onPressed: controller.editSelectedEventAction,
 | 
			
		||||
                      ),
 | 
			
		||||
                    IconButton(
 | 
			
		||||
                      icon: Icon(Icons.copy_outlined),
 | 
			
		||||
                      icon: const Icon(Icons.copy_outlined),
 | 
			
		||||
                      tooltip: L10n.of(context).copy,
 | 
			
		||||
                      onPressed: controller.copyEventsAction,
 | 
			
		||||
                    ),
 | 
			
		||||
                    if (controller.selectedEvents.length == 1)
 | 
			
		||||
                      IconButton(
 | 
			
		||||
                        icon: Icon(Icons.report_outlined),
 | 
			
		||||
                        icon: const Icon(Icons.report_outlined),
 | 
			
		||||
                        tooltip: L10n.of(context).reportMessage,
 | 
			
		||||
                        onPressed: controller.reportEventAction,
 | 
			
		||||
                      ),
 | 
			
		||||
                    if (controller.canRedactSelectedEvents)
 | 
			
		||||
                      IconButton(
 | 
			
		||||
                        icon: Icon(Icons.delete_outlined),
 | 
			
		||||
                        icon: const Icon(Icons.delete_outlined),
 | 
			
		||||
                        tooltip: L10n.of(context).redactMessage,
 | 
			
		||||
                        onPressed: controller.redactEventsAction,
 | 
			
		||||
                      ),
 | 
			
		||||
@ -176,7 +176,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                    if (controller.room.canSendDefaultStates)
 | 
			
		||||
                      IconButton(
 | 
			
		||||
                        tooltip: L10n.of(context).videoCall,
 | 
			
		||||
                        icon: Icon(Icons.video_call_outlined),
 | 
			
		||||
                        icon: const Icon(Icons.video_call_outlined),
 | 
			
		||||
                        onPressed: controller.startCallAction,
 | 
			
		||||
                      ),
 | 
			
		||||
                    ChatSettingsPopupMenu(
 | 
			
		||||
@ -211,7 +211,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                  children: <Widget>[
 | 
			
		||||
                    if (controller.room.getState(EventTypes.RoomTombstone) !=
 | 
			
		||||
                        null)
 | 
			
		||||
                      Container(
 | 
			
		||||
                      SizedBox(
 | 
			
		||||
                        height: 72,
 | 
			
		||||
                        child: Material(
 | 
			
		||||
                          color: Theme.of(context).secondaryHeaderColor,
 | 
			
		||||
@ -222,7 +222,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                  Theme.of(context).colorScheme.secondary,
 | 
			
		||||
                              backgroundColor:
 | 
			
		||||
                                  Theme.of(context).backgroundColor,
 | 
			
		||||
                              child: Icon(Icons.upgrade_outlined),
 | 
			
		||||
                              child: const Icon(Icons.upgrade_outlined),
 | 
			
		||||
                            ),
 | 
			
		||||
                            title: Text(
 | 
			
		||||
                              controller.room
 | 
			
		||||
@ -242,7 +242,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                        future: controller.getTimeline(),
 | 
			
		||||
                        builder: (BuildContext context, snapshot) {
 | 
			
		||||
                          if (controller.timeline == null) {
 | 
			
		||||
                            return Center(
 | 
			
		||||
                            return const Center(
 | 
			
		||||
                              child: CircularProgressIndicator.adaptive(
 | 
			
		||||
                                  strokeWidth: 2),
 | 
			
		||||
                            );
 | 
			
		||||
@ -266,7 +266,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                          );
 | 
			
		||||
 | 
			
		||||
                          return ListView.custom(
 | 
			
		||||
                            padding: EdgeInsets.only(
 | 
			
		||||
                            padding: const EdgeInsets.only(
 | 
			
		||||
                              top: 16,
 | 
			
		||||
                              bottom: 4,
 | 
			
		||||
                            ),
 | 
			
		||||
@ -279,7 +279,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                              (BuildContext context, int i) {
 | 
			
		||||
                                return i == controller.filteredEvents.length + 1
 | 
			
		||||
                                    ? controller.timeline.isRequestingHistory
 | 
			
		||||
                                        ? Center(
 | 
			
		||||
                                        ? const Center(
 | 
			
		||||
                                            child: CircularProgressIndicator
 | 
			
		||||
                                                .adaptive(strokeWidth: 2),
 | 
			
		||||
                                          )
 | 
			
		||||
@ -303,8 +303,8 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                        ? AnimatedContainer(
 | 
			
		||||
                                            height: seenByText.isEmpty ? 0 : 24,
 | 
			
		||||
                                            duration: seenByText.isEmpty
 | 
			
		||||
                                                ? Duration(milliseconds: 0)
 | 
			
		||||
                                                : Duration(milliseconds: 300),
 | 
			
		||||
                                                ? const Duration(milliseconds: 0)
 | 
			
		||||
                                                : const Duration(milliseconds: 300),
 | 
			
		||||
                                            alignment: controller.filteredEvents
 | 
			
		||||
                                                        .isNotEmpty &&
 | 
			
		||||
                                                    controller.filteredEvents
 | 
			
		||||
@ -312,13 +312,13 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                                        client.userID
 | 
			
		||||
                                                ? Alignment.topRight
 | 
			
		||||
                                                : Alignment.topLeft,
 | 
			
		||||
                                            padding: EdgeInsets.only(
 | 
			
		||||
                                            padding: const EdgeInsets.only(
 | 
			
		||||
                                              left: 8,
 | 
			
		||||
                                              right: 8,
 | 
			
		||||
                                              bottom: 8,
 | 
			
		||||
                                            ),
 | 
			
		||||
                                            child: Container(
 | 
			
		||||
                                              padding: EdgeInsets.symmetric(
 | 
			
		||||
                                              padding: const EdgeInsets.symmetric(
 | 
			
		||||
                                                  horizontal: 4),
 | 
			
		||||
                                              decoration: BoxDecoration(
 | 
			
		||||
                                                color: Theme.of(context)
 | 
			
		||||
@ -349,7 +349,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                              key: ValueKey(controller
 | 
			
		||||
                                                  .filteredEvents[i - 1]
 | 
			
		||||
                                                  .eventId),
 | 
			
		||||
                                              background: Padding(
 | 
			
		||||
                                              background: const Padding(
 | 
			
		||||
                                                padding: EdgeInsets.symmetric(
 | 
			
		||||
                                                    horizontal: 12.0),
 | 
			
		||||
                                                child: Center(
 | 
			
		||||
@ -409,10 +409,10 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                        },
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                    ConnectionStatusHeader(),
 | 
			
		||||
                    const ConnectionStatusHeader(),
 | 
			
		||||
                    if (!controller.showEmojiPicker)
 | 
			
		||||
                      AnimatedContainer(
 | 
			
		||||
                        duration: Duration(milliseconds: 300),
 | 
			
		||||
                        duration: const Duration(milliseconds: 300),
 | 
			
		||||
                        height: (controller.editEvent == null &&
 | 
			
		||||
                                controller.replyEvent == null &&
 | 
			
		||||
                                controller.room.canSendDefaultMessages &&
 | 
			
		||||
@ -437,12 +437,12 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                        event.room.client.userID &&
 | 
			
		||||
                                    event.type == 'm.reaction');
 | 
			
		||||
 | 
			
		||||
                            allReactionEvents.forEach((event) {
 | 
			
		||||
                            for (final event in allReactionEvents) {
 | 
			
		||||
                              try {
 | 
			
		||||
                                emojis.remove(
 | 
			
		||||
                                    event.content['m.relates_to']['key']);
 | 
			
		||||
                              } catch (_) {}
 | 
			
		||||
                            });
 | 
			
		||||
                            }
 | 
			
		||||
                            return ListView.builder(
 | 
			
		||||
                              scrollDirection: Axis.horizontal,
 | 
			
		||||
                              itemCount: emojis.length + 1,
 | 
			
		||||
@ -455,7 +455,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                        width: 56,
 | 
			
		||||
                                        height: 56,
 | 
			
		||||
                                        alignment: Alignment.center,
 | 
			
		||||
                                        child: Icon(Icons.add_outlined),
 | 
			
		||||
                                        child: const Icon(Icons.add_outlined),
 | 
			
		||||
                                      ),
 | 
			
		||||
                                    )
 | 
			
		||||
                                  : InkWell(
 | 
			
		||||
@ -468,7 +468,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                        alignment: Alignment.center,
 | 
			
		||||
                                        child: Text(
 | 
			
		||||
                                          emojis[i],
 | 
			
		||||
                                          style: TextStyle(fontSize: 30),
 | 
			
		||||
                                          style: const TextStyle(fontSize: 30),
 | 
			
		||||
                                        ),
 | 
			
		||||
                                      ),
 | 
			
		||||
                                    ),
 | 
			
		||||
@ -477,7 +477,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    AnimatedContainer(
 | 
			
		||||
                      duration: Duration(milliseconds: 300),
 | 
			
		||||
                      duration: const Duration(milliseconds: 300),
 | 
			
		||||
                      height: controller.editEvent != null ||
 | 
			
		||||
                              controller.replyEvent != null
 | 
			
		||||
                          ? 56
 | 
			
		||||
@ -488,7 +488,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                          children: <Widget>[
 | 
			
		||||
                            IconButton(
 | 
			
		||||
                              tooltip: L10n.of(context).close,
 | 
			
		||||
                              icon: Icon(Icons.close),
 | 
			
		||||
                              icon: const Icon(Icons.close),
 | 
			
		||||
                              onPressed: controller.cancelReplyEventAction,
 | 
			
		||||
                            ),
 | 
			
		||||
                            Expanded(
 | 
			
		||||
@ -502,7 +502,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                    Divider(height: 1),
 | 
			
		||||
                    const Divider(height: 1),
 | 
			
		||||
                    if (controller.room.canSendDefaultMessages &&
 | 
			
		||||
                        controller.room.membership == Membership.join &&
 | 
			
		||||
                        !controller.showEmojiPicker)
 | 
			
		||||
@ -515,13 +515,13 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
			
		||||
                          children: controller.selectMode
 | 
			
		||||
                              ? <Widget>[
 | 
			
		||||
                                  Container(
 | 
			
		||||
                                  SizedBox(
 | 
			
		||||
                                    height: 56,
 | 
			
		||||
                                    child: TextButton(
 | 
			
		||||
                                      onPressed: controller.forwardEventsAction,
 | 
			
		||||
                                      child: Row(
 | 
			
		||||
                                        children: <Widget>[
 | 
			
		||||
                                          Icon(Icons
 | 
			
		||||
                                          const Icon(Icons
 | 
			
		||||
                                              .keyboard_arrow_left_outlined),
 | 
			
		||||
                                          Text(L10n.of(context).forward),
 | 
			
		||||
                                        ],
 | 
			
		||||
@ -534,7 +534,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                                      controller.timeline)
 | 
			
		||||
                                                  .status >
 | 
			
		||||
                                              0
 | 
			
		||||
                                          ? Container(
 | 
			
		||||
                                          ? SizedBox(
 | 
			
		||||
                                              height: 56,
 | 
			
		||||
                                              child: TextButton(
 | 
			
		||||
                                                onPressed:
 | 
			
		||||
@ -543,13 +543,13 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                                  children: <Widget>[
 | 
			
		||||
                                                    Text(
 | 
			
		||||
                                                        L10n.of(context).reply),
 | 
			
		||||
                                                    Icon(Icons
 | 
			
		||||
                                                    const Icon(Icons
 | 
			
		||||
                                                        .keyboard_arrow_right),
 | 
			
		||||
                                                  ],
 | 
			
		||||
                                                ),
 | 
			
		||||
                                              ),
 | 
			
		||||
                                            )
 | 
			
		||||
                                          : Container(
 | 
			
		||||
                                          : SizedBox(
 | 
			
		||||
                                              height: 56,
 | 
			
		||||
                                              child: TextButton(
 | 
			
		||||
                                                onPressed:
 | 
			
		||||
@ -558,8 +558,8 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                                  children: <Widget>[
 | 
			
		||||
                                                    Text(L10n.of(context)
 | 
			
		||||
                                                        .tryToSendAgain),
 | 
			
		||||
                                                    SizedBox(width: 4),
 | 
			
		||||
                                                    Icon(Icons.send_outlined,
 | 
			
		||||
                                                    const SizedBox(width: 4),
 | 
			
		||||
                                                    const Icon(Icons.send_outlined,
 | 
			
		||||
                                                        size: 16),
 | 
			
		||||
                                                  ],
 | 
			
		||||
                                                ),
 | 
			
		||||
@ -569,15 +569,15 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                ]
 | 
			
		||||
                              : <Widget>[
 | 
			
		||||
                                  AnimatedContainer(
 | 
			
		||||
                                    duration: Duration(milliseconds: 200),
 | 
			
		||||
                                    duration: const Duration(milliseconds: 200),
 | 
			
		||||
                                    height: 56,
 | 
			
		||||
                                    width:
 | 
			
		||||
                                        controller.inputText.isEmpty ? 56 : 0,
 | 
			
		||||
                                    alignment: Alignment.center,
 | 
			
		||||
                                    clipBehavior: Clip.hardEdge,
 | 
			
		||||
                                    decoration: BoxDecoration(),
 | 
			
		||||
                                    decoration: const BoxDecoration(),
 | 
			
		||||
                                    child: PopupMenuButton<String>(
 | 
			
		||||
                                      icon: Icon(Icons.add_outlined),
 | 
			
		||||
                                      icon: const Icon(Icons.add_outlined),
 | 
			
		||||
                                      onSelected: controller
 | 
			
		||||
                                          .onAddPopupMenuButtonSelected,
 | 
			
		||||
                                      itemBuilder: (BuildContext context) =>
 | 
			
		||||
@ -585,7 +585,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                        PopupMenuItem<String>(
 | 
			
		||||
                                          value: 'file',
 | 
			
		||||
                                          child: ListTile(
 | 
			
		||||
                                            leading: CircleAvatar(
 | 
			
		||||
                                            leading: const CircleAvatar(
 | 
			
		||||
                                              backgroundColor: Colors.green,
 | 
			
		||||
                                              foregroundColor: Colors.white,
 | 
			
		||||
                                              child: Icon(
 | 
			
		||||
@ -593,27 +593,27 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                            ),
 | 
			
		||||
                                            title:
 | 
			
		||||
                                                Text(L10n.of(context).sendFile),
 | 
			
		||||
                                            contentPadding: EdgeInsets.all(0),
 | 
			
		||||
                                            contentPadding: const EdgeInsets.all(0),
 | 
			
		||||
                                          ),
 | 
			
		||||
                                        ),
 | 
			
		||||
                                        PopupMenuItem<String>(
 | 
			
		||||
                                          value: 'image',
 | 
			
		||||
                                          child: ListTile(
 | 
			
		||||
                                            leading: CircleAvatar(
 | 
			
		||||
                                            leading: const CircleAvatar(
 | 
			
		||||
                                              backgroundColor: Colors.blue,
 | 
			
		||||
                                              foregroundColor: Colors.white,
 | 
			
		||||
                                              child: Icon(Icons.image_outlined),
 | 
			
		||||
                                            ),
 | 
			
		||||
                                            title: Text(
 | 
			
		||||
                                                L10n.of(context).sendImage),
 | 
			
		||||
                                            contentPadding: EdgeInsets.all(0),
 | 
			
		||||
                                            contentPadding: const EdgeInsets.all(0),
 | 
			
		||||
                                          ),
 | 
			
		||||
                                        ),
 | 
			
		||||
                                        if (PlatformInfos.isMobile)
 | 
			
		||||
                                          PopupMenuItem<String>(
 | 
			
		||||
                                            value: 'camera',
 | 
			
		||||
                                            child: ListTile(
 | 
			
		||||
                                              leading: CircleAvatar(
 | 
			
		||||
                                              leading: const CircleAvatar(
 | 
			
		||||
                                                backgroundColor: Colors.purple,
 | 
			
		||||
                                                foregroundColor: Colors.white,
 | 
			
		||||
                                                child: Icon(
 | 
			
		||||
@ -621,7 +621,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                              ),
 | 
			
		||||
                                              title: Text(
 | 
			
		||||
                                                  L10n.of(context).openCamera),
 | 
			
		||||
                                              contentPadding: EdgeInsets.all(0),
 | 
			
		||||
                                              contentPadding: const EdgeInsets.all(0),
 | 
			
		||||
                                            ),
 | 
			
		||||
                                          ),
 | 
			
		||||
                                        if (controller.room
 | 
			
		||||
@ -631,7 +631,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                          PopupMenuItem<String>(
 | 
			
		||||
                                            value: 'sticker',
 | 
			
		||||
                                            child: ListTile(
 | 
			
		||||
                                              leading: CircleAvatar(
 | 
			
		||||
                                              leading: const CircleAvatar(
 | 
			
		||||
                                                backgroundColor: Colors.orange,
 | 
			
		||||
                                                foregroundColor: Colors.white,
 | 
			
		||||
                                                child: Icon(Icons
 | 
			
		||||
@ -639,14 +639,14 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                              ),
 | 
			
		||||
                                              title: Text(
 | 
			
		||||
                                                  L10n.of(context).sendSticker),
 | 
			
		||||
                                              contentPadding: EdgeInsets.all(0),
 | 
			
		||||
                                              contentPadding: const EdgeInsets.all(0),
 | 
			
		||||
                                            ),
 | 
			
		||||
                                          ),
 | 
			
		||||
                                        if (PlatformInfos.isMobile)
 | 
			
		||||
                                          PopupMenuItem<String>(
 | 
			
		||||
                                            value: 'voice',
 | 
			
		||||
                                            child: ListTile(
 | 
			
		||||
                                              leading: CircleAvatar(
 | 
			
		||||
                                              leading: const CircleAvatar(
 | 
			
		||||
                                                backgroundColor: Colors.red,
 | 
			
		||||
                                                foregroundColor: Colors.white,
 | 
			
		||||
                                                child: Icon(
 | 
			
		||||
@ -654,14 +654,14 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                              ),
 | 
			
		||||
                                              title: Text(L10n.of(context)
 | 
			
		||||
                                                  .voiceMessage),
 | 
			
		||||
                                              contentPadding: EdgeInsets.all(0),
 | 
			
		||||
                                              contentPadding: const EdgeInsets.all(0),
 | 
			
		||||
                                            ),
 | 
			
		||||
                                          ),
 | 
			
		||||
                                        if (PlatformInfos.isMobile)
 | 
			
		||||
                                          PopupMenuItem<String>(
 | 
			
		||||
                                            value: 'location',
 | 
			
		||||
                                            child: ListTile(
 | 
			
		||||
                                              leading: CircleAvatar(
 | 
			
		||||
                                              leading: const CircleAvatar(
 | 
			
		||||
                                                backgroundColor: Colors.brown,
 | 
			
		||||
                                                foregroundColor: Colors.white,
 | 
			
		||||
                                                child: Icon(
 | 
			
		||||
@ -669,7 +669,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                              ),
 | 
			
		||||
                                              title: Text(L10n.of(context)
 | 
			
		||||
                                                  .shareLocation),
 | 
			
		||||
                                              contentPadding: EdgeInsets.all(0),
 | 
			
		||||
                                              contentPadding: const EdgeInsets.all(0),
 | 
			
		||||
                                            ),
 | 
			
		||||
                                          ),
 | 
			
		||||
                                      ],
 | 
			
		||||
@ -725,7 +725,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                      alignment: Alignment.center,
 | 
			
		||||
                                      child: IconButton(
 | 
			
		||||
                                        tooltip: L10n.of(context).voiceMessage,
 | 
			
		||||
                                        icon: Icon(Icons.mic_none_outlined),
 | 
			
		||||
                                        icon: const Icon(Icons.mic_none_outlined),
 | 
			
		||||
                                        onPressed:
 | 
			
		||||
                                            controller.voiceMessageAction,
 | 
			
		||||
                                      ),
 | 
			
		||||
@ -736,7 +736,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                                      height: 56,
 | 
			
		||||
                                      alignment: Alignment.center,
 | 
			
		||||
                                      child: IconButton(
 | 
			
		||||
                                        icon: Icon(Icons.send_outlined),
 | 
			
		||||
                                        icon: const Icon(Icons.send_outlined),
 | 
			
		||||
                                        onPressed: controller.send,
 | 
			
		||||
                                        tooltip: L10n.of(context).send,
 | 
			
		||||
                                      ),
 | 
			
		||||
@ -745,7 +745,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    AnimatedContainer(
 | 
			
		||||
                      duration: Duration(milliseconds: 300),
 | 
			
		||||
                      duration: const Duration(milliseconds: 300),
 | 
			
		||||
                      height: controller.showEmojiPicker
 | 
			
		||||
                          ? MediaQuery.of(context).size.height / 2
 | 
			
		||||
                          : 0,
 | 
			
		||||
@ -770,7 +770,7 @@ class ChatView extends StatelessWidget {
 | 
			
		||||
class _EditContent extends StatelessWidget {
 | 
			
		||||
  final Event event;
 | 
			
		||||
 | 
			
		||||
  _EditContent(this.event);
 | 
			
		||||
  const _EditContent(this.event);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
@ -840,7 +840,7 @@ class _ChatAccountPicker extends StatelessWidget {
 | 
			
		||||
                        ),
 | 
			
		||||
                        title:
 | 
			
		||||
                            Text(snapshot.data?.displayName ?? client.userID),
 | 
			
		||||
                        contentPadding: EdgeInsets.all(0),
 | 
			
		||||
                        contentPadding: const EdgeInsets.all(0),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ))
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@ class DevicesSettingsView extends StatelessWidget {
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading: BackButton(),
 | 
			
		||||
        leading: const BackButton(),
 | 
			
		||||
        title: Text(L10n.of(context).devices),
 | 
			
		||||
      ),
 | 
			
		||||
      body: MaxWidthBody(
 | 
			
		||||
@ -26,14 +26,14 @@ class DevicesSettingsView extends StatelessWidget {
 | 
			
		||||
                child: Column(
 | 
			
		||||
                  mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                  children: <Widget>[
 | 
			
		||||
                    Icon(Icons.error_outlined),
 | 
			
		||||
                    const Icon(Icons.error_outlined),
 | 
			
		||||
                    Text(snapshot.error.toString()),
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
              );
 | 
			
		||||
            }
 | 
			
		||||
            if (!snapshot.hasData || controller.devices == null) {
 | 
			
		||||
              return Center(
 | 
			
		||||
              return const Center(
 | 
			
		||||
                  child: CircularProgressIndicator.adaptive(strokeWidth: 2));
 | 
			
		||||
            }
 | 
			
		||||
            return Column(
 | 
			
		||||
@ -47,23 +47,23 @@ class DevicesSettingsView extends StatelessWidget {
 | 
			
		||||
                    block: controller.blockDeviceAction,
 | 
			
		||||
                    unblock: controller.unblockDeviceAction,
 | 
			
		||||
                  ),
 | 
			
		||||
                Divider(height: 1),
 | 
			
		||||
                const Divider(height: 1),
 | 
			
		||||
                if (controller.notThisDevice.isNotEmpty)
 | 
			
		||||
                  ListTile(
 | 
			
		||||
                    title: Text(
 | 
			
		||||
                      controller.errorDeletingDevices ??
 | 
			
		||||
                          L10n.of(context).removeAllOtherDevices,
 | 
			
		||||
                      style: TextStyle(color: Colors.red),
 | 
			
		||||
                      style: const TextStyle(color: Colors.red),
 | 
			
		||||
                    ),
 | 
			
		||||
                    trailing: controller.loadingDeletingDevices
 | 
			
		||||
                        ? CircularProgressIndicator.adaptive(strokeWidth: 2)
 | 
			
		||||
                        : Icon(Icons.delete_outline),
 | 
			
		||||
                        ? const CircularProgressIndicator.adaptive(strokeWidth: 2)
 | 
			
		||||
                        : const Icon(Icons.delete_outline),
 | 
			
		||||
                    onTap: controller.loadingDeletingDevices
 | 
			
		||||
                        ? null
 | 
			
		||||
                        : () => controller
 | 
			
		||||
                            .removeDevicesAction(controller.notThisDevice),
 | 
			
		||||
                  ),
 | 
			
		||||
                Divider(height: 1),
 | 
			
		||||
                const Divider(height: 1),
 | 
			
		||||
                Expanded(
 | 
			
		||||
                  child: controller.notThisDevice.isEmpty
 | 
			
		||||
                      ? Center(
 | 
			
		||||
@ -75,7 +75,7 @@ class DevicesSettingsView extends StatelessWidget {
 | 
			
		||||
                        )
 | 
			
		||||
                      : ListView.separated(
 | 
			
		||||
                          separatorBuilder: (BuildContext context, int i) =>
 | 
			
		||||
                              Divider(height: 1),
 | 
			
		||||
                              const Divider(height: 1),
 | 
			
		||||
                          itemCount: controller.notThisDevice.length,
 | 
			
		||||
                          itemBuilder: (BuildContext context, int i) =>
 | 
			
		||||
                              UserDeviceListItem(
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@ class EmptyPage extends StatelessWidget {
 | 
			
		||||
            Center(
 | 
			
		||||
              child: SizedBox(
 | 
			
		||||
                width: _width,
 | 
			
		||||
                child: LinearProgressIndicator(),
 | 
			
		||||
                child: const LinearProgressIndicator(),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
        ],
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ class HomeserverPickerView extends StatelessWidget {
 | 
			
		||||
            prefixText: 'https://',
 | 
			
		||||
            hintText: L10n.of(context).enterYourHomeserver,
 | 
			
		||||
            searchController: controller.homeserverController,
 | 
			
		||||
            suffix: Icon(Icons.edit_outlined),
 | 
			
		||||
            suffix: const Icon(Icons.edit_outlined),
 | 
			
		||||
            padding: EdgeInsets.zero,
 | 
			
		||||
            onChanged: controller.setDomain,
 | 
			
		||||
            readOnly: !AppConfig.allowOtherHomeservers,
 | 
			
		||||
@ -44,12 +44,12 @@ class HomeserverPickerView extends StatelessWidget {
 | 
			
		||||
          elevation: 0,
 | 
			
		||||
        ),
 | 
			
		||||
        body: ListView(children: [
 | 
			
		||||
          Hero(
 | 
			
		||||
          const Hero(
 | 
			
		||||
            tag: 'loginBanner',
 | 
			
		||||
            child: FluffyBanner(),
 | 
			
		||||
          ),
 | 
			
		||||
          controller.isLoading
 | 
			
		||||
              ? Center(
 | 
			
		||||
              ? const Center(
 | 
			
		||||
                  child: CircularProgressIndicator.adaptive(strokeWidth: 2))
 | 
			
		||||
              : controller.error != null
 | 
			
		||||
                  ? Center(
 | 
			
		||||
@ -77,7 +77,7 @@ class HomeserverPickerView extends StatelessWidget {
 | 
			
		||||
                          );
 | 
			
		||||
                        }
 | 
			
		||||
                        if (!snapshot.hasData) {
 | 
			
		||||
                          return Center(
 | 
			
		||||
                          return const Center(
 | 
			
		||||
                              child: CircularProgressIndicator.adaptive(
 | 
			
		||||
                                  strokeWidth: 2));
 | 
			
		||||
                        }
 | 
			
		||||
@ -98,7 +98,7 @@ class HomeserverPickerView extends StatelessWidget {
 | 
			
		||||
                                      onPressed: () => controller
 | 
			
		||||
                                          .ssoLoginAction(identityProvider.id),
 | 
			
		||||
                                      icon: identityProvider.icon == null
 | 
			
		||||
                                          ? Icon(Icons.web_outlined)
 | 
			
		||||
                                          ? const Icon(Icons.web_outlined)
 | 
			
		||||
                                          : CachedNetworkImage(
 | 
			
		||||
                                              imageUrl: Uri.parse(
 | 
			
		||||
                                                      identityProvider.icon)
 | 
			
		||||
@ -118,12 +118,12 @@ class HomeserverPickerView extends StatelessWidget {
 | 
			
		||||
                                if (controller.registrationSupported ||
 | 
			
		||||
                                    controller.passwordLoginSupported)
 | 
			
		||||
                                  Row(children: [
 | 
			
		||||
                                    Expanded(child: Divider()),
 | 
			
		||||
                                    const Expanded(child: Divider()),
 | 
			
		||||
                                    Padding(
 | 
			
		||||
                                      padding: const EdgeInsets.all(12.0),
 | 
			
		||||
                                      child: Text(L10n.of(context).or),
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    Expanded(child: Divider()),
 | 
			
		||||
                                    const Expanded(child: Divider()),
 | 
			
		||||
                                  ]),
 | 
			
		||||
                              },
 | 
			
		||||
                              Row(
 | 
			
		||||
@ -133,13 +133,13 @@ class HomeserverPickerView extends StatelessWidget {
 | 
			
		||||
                                      child: _LoginButton(
 | 
			
		||||
                                        onPressed: () =>
 | 
			
		||||
                                            VRouter.of(context).to('login'),
 | 
			
		||||
                                        icon: Icon(Icons.login_outlined),
 | 
			
		||||
                                        icon: const Icon(Icons.login_outlined),
 | 
			
		||||
                                        labelText: L10n.of(context).login,
 | 
			
		||||
                                      ),
 | 
			
		||||
                                    ),
 | 
			
		||||
                                  if (controller.registrationSupported &&
 | 
			
		||||
                                      controller.passwordLoginSupported)
 | 
			
		||||
                                    SizedBox(width: 12),
 | 
			
		||||
                                    const SizedBox(width: 12),
 | 
			
		||||
                                  if (controller.registrationSupported &&
 | 
			
		||||
                                      // Registration is broken on matrix.org
 | 
			
		||||
                                      Matrix.of(context)
 | 
			
		||||
@ -150,7 +150,8 @@ class HomeserverPickerView extends StatelessWidget {
 | 
			
		||||
                                    Expanded(
 | 
			
		||||
                                      child: _LoginButton(
 | 
			
		||||
                                        onPressed: controller.signUpAction,
 | 
			
		||||
                                        icon: Icon(Icons.add_box_outlined),
 | 
			
		||||
                                        icon:
 | 
			
		||||
                                            const Icon(Icons.add_box_outlined),
 | 
			
		||||
                                        labelText: L10n.of(context).register,
 | 
			
		||||
                                      ),
 | 
			
		||||
                                    ),
 | 
			
		||||
@ -160,7 +161,8 @@ class HomeserverPickerView extends StatelessWidget {
 | 
			
		||||
                                .map(
 | 
			
		||||
                                  (widget) => Container(
 | 
			
		||||
                                      height: 64,
 | 
			
		||||
                                      padding: EdgeInsets.only(bottom: 12),
 | 
			
		||||
                                      padding:
 | 
			
		||||
                                          const EdgeInsets.only(bottom: 12),
 | 
			
		||||
                                      child: widget),
 | 
			
		||||
                                )
 | 
			
		||||
                                .toList(),
 | 
			
		||||
@ -174,7 +176,7 @@ class HomeserverPickerView extends StatelessWidget {
 | 
			
		||||
                onPressed: () => launch(AppConfig.privacyUrl),
 | 
			
		||||
                child: Text(
 | 
			
		||||
                  L10n.of(context).privacy,
 | 
			
		||||
                  style: TextStyle(
 | 
			
		||||
                  style: const TextStyle(
 | 
			
		||||
                    decoration: TextDecoration.underline,
 | 
			
		||||
                    color: Colors.blueGrey,
 | 
			
		||||
                  ),
 | 
			
		||||
@ -184,7 +186,7 @@ class HomeserverPickerView extends StatelessWidget {
 | 
			
		||||
                onPressed: () => PlatformInfos.showDialog(context),
 | 
			
		||||
                child: Text(
 | 
			
		||||
                  L10n.of(context).about,
 | 
			
		||||
                  style: TextStyle(
 | 
			
		||||
                  style: const TextStyle(
 | 
			
		||||
                    decoration: TextDecoration.underline,
 | 
			
		||||
                    color: Colors.blueGrey,
 | 
			
		||||
                  ),
 | 
			
		||||
@ -213,7 +215,7 @@ class _LoginButton extends StatelessWidget {
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return OutlinedButton.icon(
 | 
			
		||||
      style: OutlinedButton.styleFrom(
 | 
			
		||||
          minimumSize: Size(256, 56),
 | 
			
		||||
          minimumSize: const Size(256, 56),
 | 
			
		||||
          side: BorderSide(
 | 
			
		||||
            color: Theme.of(context).textTheme.bodyText1.color,
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
@ -16,21 +16,21 @@ class ImageViewerView extends StatelessWidget {
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        elevation: 0,
 | 
			
		||||
        leading: IconButton(
 | 
			
		||||
          icon: Icon(Icons.close),
 | 
			
		||||
          icon: const Icon(Icons.close),
 | 
			
		||||
          onPressed: Navigator.of(context).pop,
 | 
			
		||||
          color: Colors.white,
 | 
			
		||||
          tooltip: L10n.of(context).close,
 | 
			
		||||
        ),
 | 
			
		||||
        backgroundColor: Color(0x44000000),
 | 
			
		||||
        backgroundColor: const Color(0x44000000),
 | 
			
		||||
        actions: [
 | 
			
		||||
          IconButton(
 | 
			
		||||
            icon: Icon(Icons.reply_outlined),
 | 
			
		||||
            icon: const Icon(Icons.reply_outlined),
 | 
			
		||||
            onPressed: controller.forwardAction,
 | 
			
		||||
            color: Colors.white,
 | 
			
		||||
            tooltip: L10n.of(context).share,
 | 
			
		||||
          ),
 | 
			
		||||
          IconButton(
 | 
			
		||||
            icon: Icon(Icons.download_outlined),
 | 
			
		||||
            icon: const Icon(Icons.download_outlined),
 | 
			
		||||
            onPressed: controller.saveFileAction,
 | 
			
		||||
            color: Colors.white,
 | 
			
		||||
            tooltip: L10n.of(context).downloadFile,
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,7 @@ class InvitationSelectionView extends StatelessWidget {
 | 
			
		||||
        leading: VRouter.of(context).path.startsWith('/spaces/')
 | 
			
		||||
            ? null
 | 
			
		||||
            : IconButton(
 | 
			
		||||
                icon: Icon(Icons.close_outlined),
 | 
			
		||||
                icon: const Icon(Icons.close_outlined),
 | 
			
		||||
                onPressed: () => VRouter.of(context)
 | 
			
		||||
                    .toSegments(['rooms', controller.roomId]),
 | 
			
		||||
              ),
 | 
			
		||||
@ -39,7 +39,7 @@ class InvitationSelectionView extends StatelessWidget {
 | 
			
		||||
        withScrolling: true,
 | 
			
		||||
        child: controller.foundProfiles.isNotEmpty
 | 
			
		||||
            ? ListView.builder(
 | 
			
		||||
                physics: NeverScrollableScrollPhysics(),
 | 
			
		||||
                physics: const NeverScrollableScrollPhysics(),
 | 
			
		||||
                shrinkWrap: true,
 | 
			
		||||
                itemCount: controller.foundProfiles.length,
 | 
			
		||||
                itemBuilder: (BuildContext context, int i) => ListTile(
 | 
			
		||||
@ -61,13 +61,13 @@ class InvitationSelectionView extends StatelessWidget {
 | 
			
		||||
                future: controller.getContacts(context),
 | 
			
		||||
                builder: (BuildContext context, snapshot) {
 | 
			
		||||
                  if (!snapshot.hasData) {
 | 
			
		||||
                    return Center(
 | 
			
		||||
                    return const Center(
 | 
			
		||||
                      child: CircularProgressIndicator.adaptive(strokeWidth: 2),
 | 
			
		||||
                    );
 | 
			
		||||
                  }
 | 
			
		||||
                  final contacts = snapshot.data;
 | 
			
		||||
                  return ListView.builder(
 | 
			
		||||
                    physics: NeverScrollableScrollPhysics(),
 | 
			
		||||
                    physics: const NeverScrollableScrollPhysics(),
 | 
			
		||||
                    shrinkWrap: true,
 | 
			
		||||
                    itemCount: contacts.length,
 | 
			
		||||
                    itemBuilder: (BuildContext context, int i) => ListTile(
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@ class LoginView extends StatelessWidget {
 | 
			
		||||
    return OnePageCard(
 | 
			
		||||
      child: Scaffold(
 | 
			
		||||
        appBar: AppBar(
 | 
			
		||||
          leading: controller.loading ? Container() : BackButton(),
 | 
			
		||||
          leading: controller.loading ? Container() : const BackButton(),
 | 
			
		||||
          elevation: 0,
 | 
			
		||||
          title: Text(
 | 
			
		||||
            L10n.of(context).logInTo(Matrix.of(context)
 | 
			
		||||
@ -40,7 +40,7 @@ class LoginView extends StatelessWidget {
 | 
			
		||||
                    autofillHints:
 | 
			
		||||
                        controller.loading ? null : [AutofillHints.username],
 | 
			
		||||
                    decoration: InputDecoration(
 | 
			
		||||
                        prefixIcon: Icon(Icons.account_box_outlined),
 | 
			
		||||
                        prefixIcon: const Icon(Icons.account_box_outlined),
 | 
			
		||||
                        hintText: L10n.of(context).username,
 | 
			
		||||
                        errorText: controller.usernameError,
 | 
			
		||||
                        labelText: L10n.of(context).username),
 | 
			
		||||
@ -57,7 +57,7 @@ class LoginView extends StatelessWidget {
 | 
			
		||||
                    obscureText: !controller.showPassword,
 | 
			
		||||
                    onSubmitted: controller.login,
 | 
			
		||||
                    decoration: InputDecoration(
 | 
			
		||||
                      prefixIcon: Icon(Icons.lock_outlined),
 | 
			
		||||
                      prefixIcon: const Icon(Icons.lock_outlined),
 | 
			
		||||
                      hintText: '****',
 | 
			
		||||
                      errorText: controller.passwordError,
 | 
			
		||||
                      suffixIcon: IconButton(
 | 
			
		||||
@ -71,17 +71,17 @@ class LoginView extends StatelessWidget {
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                SizedBox(height: 12),
 | 
			
		||||
                const SizedBox(height: 12),
 | 
			
		||||
                Hero(
 | 
			
		||||
                  tag: 'loginButton',
 | 
			
		||||
                  child: Padding(
 | 
			
		||||
                    padding: EdgeInsets.symmetric(horizontal: 12),
 | 
			
		||||
                    padding: const EdgeInsets.symmetric(horizontal: 12),
 | 
			
		||||
                    child: ElevatedButton(
 | 
			
		||||
                      onPressed: controller.loading
 | 
			
		||||
                          ? null
 | 
			
		||||
                          : () => controller.login(context),
 | 
			
		||||
                      child: controller.loading
 | 
			
		||||
                          ? LinearProgressIndicator()
 | 
			
		||||
                          ? const LinearProgressIndicator()
 | 
			
		||||
                          : Text(L10n.of(context).login),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
@ -91,7 +91,7 @@ class LoginView extends StatelessWidget {
 | 
			
		||||
                    onPressed: controller.passwordForgotten,
 | 
			
		||||
                    child: Text(
 | 
			
		||||
                      L10n.of(context).passwordForgotten,
 | 
			
		||||
                      style: TextStyle(
 | 
			
		||||
                      style: const TextStyle(
 | 
			
		||||
                        color: Colors.blue,
 | 
			
		||||
                        decoration: TextDecoration.underline,
 | 
			
		||||
                      ),
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ class NewGroupView extends StatelessWidget {
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading: BackButton(),
 | 
			
		||||
        leading: const BackButton(),
 | 
			
		||||
        title: Text(L10n.of(context).createNewGroup),
 | 
			
		||||
        elevation: 0,
 | 
			
		||||
      ),
 | 
			
		||||
@ -30,7 +30,7 @@ class NewGroupView extends StatelessWidget {
 | 
			
		||||
                onSubmitted: controller.submitAction,
 | 
			
		||||
                decoration: InputDecoration(
 | 
			
		||||
                    labelText: L10n.of(context).optionalGroupName,
 | 
			
		||||
                    prefixIcon: Icon(Icons.people_outlined),
 | 
			
		||||
                    prefixIcon: const Icon(Icons.people_outlined),
 | 
			
		||||
                    hintText: L10n.of(context).enterAGroupName),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
@ -47,7 +47,7 @@ class NewGroupView extends StatelessWidget {
 | 
			
		||||
      ),
 | 
			
		||||
      floatingActionButton: FloatingActionButton(
 | 
			
		||||
        onPressed: controller.submitAction,
 | 
			
		||||
        child: Icon(Icons.arrow_forward_outlined),
 | 
			
		||||
        child: const Icon(Icons.arrow_forward_outlined),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ class NewPrivateChatView extends StatelessWidget {
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading: BackButton(),
 | 
			
		||||
        leading: const BackButton(),
 | 
			
		||||
        title: Text(L10n.of(context).newChat),
 | 
			
		||||
        backgroundColor: Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
        elevation: 0,
 | 
			
		||||
@ -39,9 +39,9 @@ class NewPrivateChatView extends StatelessWidget {
 | 
			
		||||
        child: ListView(
 | 
			
		||||
          children: [
 | 
			
		||||
            Container(
 | 
			
		||||
              margin: EdgeInsets.all(_qrCodePadding),
 | 
			
		||||
              margin: const EdgeInsets.all(_qrCodePadding),
 | 
			
		||||
              alignment: Alignment.center,
 | 
			
		||||
              padding: EdgeInsets.all(_qrCodePadding * 2),
 | 
			
		||||
              padding: const EdgeInsets.all(_qrCodePadding * 2),
 | 
			
		||||
              child: InkWell(
 | 
			
		||||
                onTap: controller.inviteAction,
 | 
			
		||||
                borderRadius: BorderRadius.circular(12),
 | 
			
		||||
@ -54,25 +54,25 @@ class NewPrivateChatView extends StatelessWidget {
 | 
			
		||||
                        'https://matrix.to/#/${Matrix.of(context).client.userID}',
 | 
			
		||||
                    version: QrVersions.auto,
 | 
			
		||||
                    size: min(MediaQuery.of(context).size.width - 16, 200),
 | 
			
		||||
                    embeddedImage: AssetImage('assets/share.png'),
 | 
			
		||||
                    embeddedImage: const AssetImage('assets/share.png'),
 | 
			
		||||
                    embeddedImageStyle: QrEmbeddedImageStyle(
 | 
			
		||||
                      size: Size(48, 48),
 | 
			
		||||
                      size: const Size(48, 48),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            Divider(),
 | 
			
		||||
            const Divider(),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              subtitle: Text(L10n.of(context).createNewChatExplaination),
 | 
			
		||||
              trailing: Padding(
 | 
			
		||||
                padding: const EdgeInsets.all(8.0),
 | 
			
		||||
              trailing: const Padding(
 | 
			
		||||
                padding: EdgeInsets.all(8.0),
 | 
			
		||||
                child: Icon(Icons.info_outline_rounded),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            Divider(),
 | 
			
		||||
            const Divider(),
 | 
			
		||||
            Padding(
 | 
			
		||||
              padding: EdgeInsets.all(12),
 | 
			
		||||
              padding: const EdgeInsets.all(12),
 | 
			
		||||
              child: Form(
 | 
			
		||||
                key: controller.formKey,
 | 
			
		||||
                child: TextFormField(
 | 
			
		||||
@ -86,7 +86,7 @@ class NewPrivateChatView extends StatelessWidget {
 | 
			
		||||
                    hintText: '@username',
 | 
			
		||||
                    prefixText: 'https://matrix.to/#/',
 | 
			
		||||
                    suffixIcon: IconButton(
 | 
			
		||||
                      icon: Icon(Icons.send_outlined),
 | 
			
		||||
                      icon: const Icon(Icons.send_outlined),
 | 
			
		||||
                      onPressed: controller.submitAction,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
@ -107,7 +107,7 @@ class NewPrivateChatView extends StatelessWidget {
 | 
			
		||||
          ? FloatingActionButton.extended(
 | 
			
		||||
              onPressed: controller.openScannerAction,
 | 
			
		||||
              label: Text(L10n.of(context).scanQrCode),
 | 
			
		||||
              icon: Icon(Icons.camera_alt_outlined),
 | 
			
		||||
              icon: const Icon(Icons.camera_alt_outlined),
 | 
			
		||||
            )
 | 
			
		||||
          : null,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ class NewSpaceView extends StatelessWidget {
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading: BackButton(),
 | 
			
		||||
        leading: const BackButton(),
 | 
			
		||||
        title: Text(L10n.of(context).createNewSpace),
 | 
			
		||||
        elevation: 0,
 | 
			
		||||
      ),
 | 
			
		||||
@ -31,7 +31,7 @@ class NewSpaceView extends StatelessWidget {
 | 
			
		||||
                onSubmitted: controller.submitAction,
 | 
			
		||||
                decoration: InputDecoration(
 | 
			
		||||
                    labelText: L10n.of(context).spaceName,
 | 
			
		||||
                    prefixIcon: Icon(Icons.people_outlined),
 | 
			
		||||
                    prefixIcon: const Icon(Icons.people_outlined),
 | 
			
		||||
                    hintText: L10n.of(context).enterASpacepName),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
@ -48,7 +48,7 @@ class NewSpaceView extends StatelessWidget {
 | 
			
		||||
      ),
 | 
			
		||||
      floatingActionButton: FloatingActionButton(
 | 
			
		||||
        onPressed: controller.submitAction,
 | 
			
		||||
        child: Icon(Icons.arrow_forward_outlined),
 | 
			
		||||
        child: const Icon(Icons.arrow_forward_outlined),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -69,21 +69,21 @@ class SearchView extends StatelessWidget {
 | 
			
		||||
          controller.controller.text?.startsWith('#') ?? false ? 0 : 1,
 | 
			
		||||
      child: Scaffold(
 | 
			
		||||
        appBar: AppBar(
 | 
			
		||||
          leading: BackButton(),
 | 
			
		||||
          leading: const BackButton(),
 | 
			
		||||
          titleSpacing: 0,
 | 
			
		||||
          title: DefaultAppBarSearchField(
 | 
			
		||||
            autofocus: true,
 | 
			
		||||
            hintText: L10n.of(context).search,
 | 
			
		||||
            searchController: controller.controller,
 | 
			
		||||
            suffix: Icon(Icons.search_outlined),
 | 
			
		||||
            suffix: const Icon(Icons.search_outlined),
 | 
			
		||||
            onChanged: controller.search,
 | 
			
		||||
          ),
 | 
			
		||||
          bottom: TabBar(
 | 
			
		||||
            indicatorColor: Theme.of(context).colorScheme.secondary,
 | 
			
		||||
            labelColor: Theme.of(context).colorScheme.secondary,
 | 
			
		||||
            unselectedLabelColor: Theme.of(context).textTheme.bodyText1.color,
 | 
			
		||||
            labelStyle: TextStyle(fontSize: 16),
 | 
			
		||||
            labelPadding: EdgeInsets.symmetric(
 | 
			
		||||
            labelStyle: const TextStyle(fontSize: 16),
 | 
			
		||||
            labelPadding: const EdgeInsets.symmetric(
 | 
			
		||||
              horizontal: 8,
 | 
			
		||||
              vertical: 0,
 | 
			
		||||
            ),
 | 
			
		||||
@ -101,12 +101,12 @@ class SearchView extends StatelessWidget {
 | 
			
		||||
                  ? ScrollViewKeyboardDismissBehavior.onDrag
 | 
			
		||||
                  : ScrollViewKeyboardDismissBehavior.manual,
 | 
			
		||||
              children: [
 | 
			
		||||
                SizedBox(height: 12),
 | 
			
		||||
                const SizedBox(height: 12),
 | 
			
		||||
                ListTile(
 | 
			
		||||
                  leading: CircleAvatar(
 | 
			
		||||
                    foregroundColor: Theme.of(context).colorScheme.secondary,
 | 
			
		||||
                    backgroundColor: Theme.of(context).secondaryHeaderColor,
 | 
			
		||||
                    child: Icon(Icons.edit_outlined),
 | 
			
		||||
                    child: const Icon(Icons.edit_outlined),
 | 
			
		||||
                  ),
 | 
			
		||||
                  title: Text(L10n.of(context).changeTheServer),
 | 
			
		||||
                  onTap: controller.setServer,
 | 
			
		||||
@ -119,8 +119,8 @@ class SearchView extends StatelessWidget {
 | 
			
		||||
                        return Column(
 | 
			
		||||
                          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                          children: [
 | 
			
		||||
                            SizedBox(height: 32),
 | 
			
		||||
                            Icon(
 | 
			
		||||
                            const SizedBox(height: 32),
 | 
			
		||||
                            const Icon(
 | 
			
		||||
                              Icons.error_outlined,
 | 
			
		||||
                              size: 80,
 | 
			
		||||
                              color: Colors.grey,
 | 
			
		||||
@ -129,7 +129,7 @@ class SearchView extends StatelessWidget {
 | 
			
		||||
                              child: Text(
 | 
			
		||||
                                snapshot.error.toLocalizedString(context),
 | 
			
		||||
                                textAlign: TextAlign.center,
 | 
			
		||||
                                style: TextStyle(
 | 
			
		||||
                                style: const TextStyle(
 | 
			
		||||
                                  color: Colors.grey,
 | 
			
		||||
                                  fontSize: 16,
 | 
			
		||||
                                ),
 | 
			
		||||
@ -139,7 +139,7 @@ class SearchView extends StatelessWidget {
 | 
			
		||||
                        );
 | 
			
		||||
                      }
 | 
			
		||||
                      if (snapshot.connectionState != ConnectionState.done) {
 | 
			
		||||
                        return Center(
 | 
			
		||||
                        return const Center(
 | 
			
		||||
                            child: CircularProgressIndicator.adaptive(
 | 
			
		||||
                                strokeWidth: 2));
 | 
			
		||||
                      }
 | 
			
		||||
@ -148,8 +148,8 @@ class SearchView extends StatelessWidget {
 | 
			
		||||
                        return Column(
 | 
			
		||||
                          mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                          children: [
 | 
			
		||||
                            SizedBox(height: 32),
 | 
			
		||||
                            Icon(
 | 
			
		||||
                            const SizedBox(height: 32),
 | 
			
		||||
                            const Icon(
 | 
			
		||||
                              Icons.search_outlined,
 | 
			
		||||
                              size: 80,
 | 
			
		||||
                              color: Colors.grey,
 | 
			
		||||
@ -158,7 +158,7 @@ class SearchView extends StatelessWidget {
 | 
			
		||||
                              child: Text(
 | 
			
		||||
                                L10n.of(context).noPublicRoomsFound,
 | 
			
		||||
                                textAlign: TextAlign.center,
 | 
			
		||||
                                style: TextStyle(
 | 
			
		||||
                                style: const TextStyle(
 | 
			
		||||
                                  color: Colors.grey,
 | 
			
		||||
                                  fontSize: 16,
 | 
			
		||||
                                ),
 | 
			
		||||
@ -169,9 +169,9 @@ class SearchView extends StatelessWidget {
 | 
			
		||||
                      }
 | 
			
		||||
                      return GridView.builder(
 | 
			
		||||
                        shrinkWrap: true,
 | 
			
		||||
                        padding: EdgeInsets.all(12),
 | 
			
		||||
                        physics: NeverScrollableScrollPhysics(),
 | 
			
		||||
                        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
 | 
			
		||||
                        padding: const EdgeInsets.all(12),
 | 
			
		||||
                        physics: const NeverScrollableScrollPhysics(),
 | 
			
		||||
                        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
 | 
			
		||||
                          crossAxisCount: 2,
 | 
			
		||||
                          childAspectRatio: 1,
 | 
			
		||||
                          crossAxisSpacing: 16,
 | 
			
		||||
@ -195,7 +195,7 @@ class SearchView extends StatelessWidget {
 | 
			
		||||
                                      publicRoomsResponse.chunk[i].name),
 | 
			
		||||
                                  Text(
 | 
			
		||||
                                    publicRoomsResponse.chunk[i].name,
 | 
			
		||||
                                    style: TextStyle(
 | 
			
		||||
                                    style: const TextStyle(
 | 
			
		||||
                                      fontSize: 16,
 | 
			
		||||
                                      fontWeight: FontWeight.bold,
 | 
			
		||||
                                    ),
 | 
			
		||||
@ -207,7 +207,7 @@ class SearchView extends StatelessWidget {
 | 
			
		||||
                                        publicRoomsResponse
 | 
			
		||||
                                                .chunk[i].numJoinedMembers ??
 | 
			
		||||
                                            0),
 | 
			
		||||
                                    style: TextStyle(fontSize: 10.5),
 | 
			
		||||
                                    style: const TextStyle(fontSize: 10.5),
 | 
			
		||||
                                    maxLines: 1,
 | 
			
		||||
                                    textAlign: TextAlign.center,
 | 
			
		||||
                                  ),
 | 
			
		||||
@ -262,13 +262,13 @@ class SearchView extends StatelessWidget {
 | 
			
		||||
                        title: Text(
 | 
			
		||||
                          foundProfile.displayName ??
 | 
			
		||||
                              foundProfile.userId.localpart,
 | 
			
		||||
                          style: TextStyle(),
 | 
			
		||||
                          style: const TextStyle(),
 | 
			
		||||
                          maxLines: 1,
 | 
			
		||||
                        ),
 | 
			
		||||
                        subtitle: Text(
 | 
			
		||||
                          foundProfile.userId,
 | 
			
		||||
                          maxLines: 1,
 | 
			
		||||
                          style: TextStyle(
 | 
			
		||||
                          style: const TextStyle(
 | 
			
		||||
                            fontSize: 12,
 | 
			
		||||
                          ),
 | 
			
		||||
                        ),
 | 
			
		||||
 | 
			
		||||
@ -15,11 +15,11 @@ class Settings3PidView extends StatelessWidget {
 | 
			
		||||
    controller.request ??= Matrix.of(context).client.getAccount3PIDs();
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading: BackButton(),
 | 
			
		||||
        leading: const BackButton(),
 | 
			
		||||
        title: Text(L10n.of(context).passwordRecovery),
 | 
			
		||||
        actions: [
 | 
			
		||||
          IconButton(
 | 
			
		||||
            icon: Icon(Icons.add_outlined),
 | 
			
		||||
            icon: const Icon(Icons.add_outlined),
 | 
			
		||||
            onPressed: controller.add3PidAction,
 | 
			
		||||
            tooltip: L10n.of(context).addEmail,
 | 
			
		||||
          )
 | 
			
		||||
@ -39,7 +39,7 @@ class Settings3PidView extends StatelessWidget {
 | 
			
		||||
              );
 | 
			
		||||
            }
 | 
			
		||||
            if (!snapshot.hasData) {
 | 
			
		||||
              return Center(
 | 
			
		||||
              return const Center(
 | 
			
		||||
                  child: CircularProgressIndicator.adaptive(strokeWidth: 2));
 | 
			
		||||
            }
 | 
			
		||||
            final identifier = snapshot.data;
 | 
			
		||||
@ -63,7 +63,7 @@ class Settings3PidView extends StatelessWidget {
 | 
			
		||||
                            .withTheseAddressesRecoveryDescription,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                Divider(height: 1),
 | 
			
		||||
                const Divider(height: 1),
 | 
			
		||||
                Expanded(
 | 
			
		||||
                  child: ListView.builder(
 | 
			
		||||
                    itemCount: identifier.length,
 | 
			
		||||
@ -76,7 +76,7 @@ class Settings3PidView extends StatelessWidget {
 | 
			
		||||
                      title: Text(identifier[i].address),
 | 
			
		||||
                      trailing: IconButton(
 | 
			
		||||
                        tooltip: L10n.of(context).delete,
 | 
			
		||||
                        icon: Icon(Icons.delete_forever_outlined),
 | 
			
		||||
                        icon: const Icon(Icons.delete_forever_outlined),
 | 
			
		||||
                        color: Colors.red,
 | 
			
		||||
                        onPressed: () => controller.delete3Pid(identifier[i]),
 | 
			
		||||
                      ),
 | 
			
		||||
 | 
			
		||||
@ -22,51 +22,51 @@ class SettingsAccountView extends StatelessWidget {
 | 
			
		||||
        child: Column(
 | 
			
		||||
          children: [
 | 
			
		||||
            ListTile(
 | 
			
		||||
              trailing: Icon(Icons.add_box_outlined),
 | 
			
		||||
              trailing: const Icon(Icons.add_box_outlined),
 | 
			
		||||
              title: Text(L10n.of(context).addAccount),
 | 
			
		||||
              subtitle: Text(L10n.of(context).enableMultiAccounts),
 | 
			
		||||
              onTap: controller.addAccountAction,
 | 
			
		||||
            ),
 | 
			
		||||
            Divider(height: 1),
 | 
			
		||||
            const Divider(height: 1),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              title: Text(L10n.of(context).yourUserId),
 | 
			
		||||
              subtitle: Text(Matrix.of(context).client.userID),
 | 
			
		||||
              trailing: Icon(Icons.copy_outlined),
 | 
			
		||||
              trailing: const Icon(Icons.copy_outlined),
 | 
			
		||||
              onTap: () => FluffyShare.share(
 | 
			
		||||
                Matrix.of(context).client.userID,
 | 
			
		||||
                context,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              trailing: Icon(Icons.edit_outlined),
 | 
			
		||||
              trailing: const Icon(Icons.edit_outlined),
 | 
			
		||||
              title: Text(L10n.of(context).editDisplayname),
 | 
			
		||||
              subtitle: Text(controller.profile?.displayName ??
 | 
			
		||||
                  Matrix.of(context).client.userID.localpart),
 | 
			
		||||
              onTap: controller.setDisplaynameAction,
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              trailing: Icon(Icons.phone_outlined),
 | 
			
		||||
              trailing: const Icon(Icons.phone_outlined),
 | 
			
		||||
              title: Text(L10n.of(context).editJitsiInstance),
 | 
			
		||||
              subtitle: Text(AppConfig.jitsiInstance),
 | 
			
		||||
              onTap: controller.setJitsiInstanceAction,
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              trailing: Icon(Icons.devices_other_outlined),
 | 
			
		||||
              trailing: const Icon(Icons.devices_other_outlined),
 | 
			
		||||
              title: Text(L10n.of(context).devices),
 | 
			
		||||
              onTap: () => VRouter.of(context).to('devices'),
 | 
			
		||||
            ),
 | 
			
		||||
            Divider(height: 1),
 | 
			
		||||
            const Divider(height: 1),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              trailing: Icon(Icons.exit_to_app_outlined),
 | 
			
		||||
              trailing: const Icon(Icons.exit_to_app_outlined),
 | 
			
		||||
              title: Text(L10n.of(context).logout),
 | 
			
		||||
              onTap: controller.logoutAction,
 | 
			
		||||
            ),
 | 
			
		||||
            Divider(height: 1),
 | 
			
		||||
            const Divider(height: 1),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              trailing: Icon(Icons.delete_forever_outlined),
 | 
			
		||||
              trailing: const Icon(Icons.delete_forever_outlined),
 | 
			
		||||
              title: Text(
 | 
			
		||||
                L10n.of(context).deleteAccount,
 | 
			
		||||
                style: TextStyle(color: Colors.red),
 | 
			
		||||
                style: const TextStyle(color: Colors.red),
 | 
			
		||||
              ),
 | 
			
		||||
              onTap: controller.deleteAccountAction,
 | 
			
		||||
            ),
 | 
			
		||||
 | 
			
		||||
@ -24,14 +24,14 @@ class SettingsChatView extends StatelessWidget {
 | 
			
		||||
            ListTile(
 | 
			
		||||
              title: Text(L10n.of(context).changeTheme),
 | 
			
		||||
              onTap: () => VRouter.of(context).to('style'),
 | 
			
		||||
              trailing: Icon(Icons.style_outlined),
 | 
			
		||||
              trailing: const Icon(Icons.style_outlined),
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              title: Text(L10n.of(context).emoteSettings),
 | 
			
		||||
              onTap: () => VRouter.of(context).to('emotes'),
 | 
			
		||||
              trailing: Icon(Icons.insert_emoticon_outlined),
 | 
			
		||||
              trailing: const Icon(Icons.insert_emoticon_outlined),
 | 
			
		||||
            ),
 | 
			
		||||
            Divider(height: 1),
 | 
			
		||||
            const Divider(height: 1),
 | 
			
		||||
            SettingsSwitchListTile(
 | 
			
		||||
              title: L10n.of(context).renderRichContent,
 | 
			
		||||
              onChanged: (b) => AppConfig.renderHtml = b,
 | 
			
		||||
 | 
			
		||||
@ -20,13 +20,13 @@ class EmotesSettingsView extends StatelessWidget {
 | 
			
		||||
    final imageKeys = controller.pack.images.keys.toList();
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading: BackButton(),
 | 
			
		||||
        leading: const BackButton(),
 | 
			
		||||
        title: Text(L10n.of(context).emoteSettings),
 | 
			
		||||
      ),
 | 
			
		||||
      floatingActionButton: controller.showSave
 | 
			
		||||
          ? FloatingActionButton(
 | 
			
		||||
              onPressed: controller.saveAction,
 | 
			
		||||
              child: Icon(Icons.save_outlined, color: Colors.white),
 | 
			
		||||
              child: const Icon(Icons.save_outlined, color: Colors.white),
 | 
			
		||||
            )
 | 
			
		||||
          : null,
 | 
			
		||||
      body: MaxWidthBody(
 | 
			
		||||
@ -34,16 +34,16 @@ class EmotesSettingsView extends StatelessWidget {
 | 
			
		||||
          children: <Widget>[
 | 
			
		||||
            if (!controller.readonly)
 | 
			
		||||
              Container(
 | 
			
		||||
                padding: EdgeInsets.symmetric(
 | 
			
		||||
                padding: const EdgeInsets.symmetric(
 | 
			
		||||
                  vertical: 8.0,
 | 
			
		||||
                ),
 | 
			
		||||
                child: ListTile(
 | 
			
		||||
                  leading: Container(
 | 
			
		||||
                    width: 180.0,
 | 
			
		||||
                    height: 38,
 | 
			
		||||
                    padding: EdgeInsets.symmetric(horizontal: 8),
 | 
			
		||||
                    padding: const EdgeInsets.symmetric(horizontal: 8),
 | 
			
		||||
                    decoration: BoxDecoration(
 | 
			
		||||
                      borderRadius: BorderRadius.all(Radius.circular(10)),
 | 
			
		||||
                      borderRadius: const BorderRadius.all(Radius.circular(10)),
 | 
			
		||||
                      color: Theme.of(context).secondaryHeaderColor,
 | 
			
		||||
                    ),
 | 
			
		||||
                    child: TextField(
 | 
			
		||||
@ -73,7 +73,7 @@ class EmotesSettingsView extends StatelessWidget {
 | 
			
		||||
                  ),
 | 
			
		||||
                  trailing: InkWell(
 | 
			
		||||
                    onTap: controller.addImageAction,
 | 
			
		||||
                    child: Icon(
 | 
			
		||||
                    child: const Icon(
 | 
			
		||||
                      Icons.add_outlined,
 | 
			
		||||
                      color: Colors.green,
 | 
			
		||||
                      size: 32.0,
 | 
			
		||||
@ -99,10 +99,10 @@ class EmotesSettingsView extends StatelessWidget {
 | 
			
		||||
              child: imageKeys.isEmpty
 | 
			
		||||
                  ? Center(
 | 
			
		||||
                      child: Padding(
 | 
			
		||||
                        padding: EdgeInsets.all(16),
 | 
			
		||||
                        padding: const EdgeInsets.all(16),
 | 
			
		||||
                        child: Text(
 | 
			
		||||
                          L10n.of(context).noEmotesFound,
 | 
			
		||||
                          style: TextStyle(fontSize: 20),
 | 
			
		||||
                          style: const TextStyle(fontSize: 20),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    )
 | 
			
		||||
@ -124,10 +124,10 @@ class EmotesSettingsView extends StatelessWidget {
 | 
			
		||||
                          leading: Container(
 | 
			
		||||
                            width: 180.0,
 | 
			
		||||
                            height: 38,
 | 
			
		||||
                            padding: EdgeInsets.symmetric(horizontal: 8),
 | 
			
		||||
                            padding: const EdgeInsets.symmetric(horizontal: 8),
 | 
			
		||||
                            decoration: BoxDecoration(
 | 
			
		||||
                              borderRadius:
 | 
			
		||||
                                  BorderRadius.all(Radius.circular(10)),
 | 
			
		||||
                                  const BorderRadius.all(Radius.circular(10)),
 | 
			
		||||
                              color: Theme.of(context).secondaryHeaderColor,
 | 
			
		||||
                            ),
 | 
			
		||||
                            child: Shortcuts(
 | 
			
		||||
@ -193,7 +193,7 @@ class EmotesSettingsView extends StatelessWidget {
 | 
			
		||||
                              : InkWell(
 | 
			
		||||
                                  onTap: () =>
 | 
			
		||||
                                      controller.removeImageAction(imageCode),
 | 
			
		||||
                                  child: Icon(
 | 
			
		||||
                                  child: const Icon(
 | 
			
		||||
                                    Icons.delete_forever_outlined,
 | 
			
		||||
                                    color: Colors.red,
 | 
			
		||||
                                    size: 32.0,
 | 
			
		||||
@ -212,11 +212,11 @@ class EmotesSettingsView extends StatelessWidget {
 | 
			
		||||
 | 
			
		||||
class _EmoteImage extends StatelessWidget {
 | 
			
		||||
  final Uri mxc;
 | 
			
		||||
  _EmoteImage(this.mxc);
 | 
			
		||||
  const _EmoteImage(this.mxc);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final size = 38.0;
 | 
			
		||||
    const size = 38.0;
 | 
			
		||||
    final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
 | 
			
		||||
    final url = mxc?.getThumbnail(
 | 
			
		||||
      Matrix.of(context).client,
 | 
			
		||||
@ -238,7 +238,7 @@ class _ImagePicker extends StatefulWidget {
 | 
			
		||||
 | 
			
		||||
  final void Function(ValueNotifier<ImagePackImageContent>) onPressed;
 | 
			
		||||
 | 
			
		||||
  _ImagePicker({@required this.controller, @required this.onPressed});
 | 
			
		||||
  const _ImagePicker({@required this.controller, @required this.onPressed});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  _ImagePickerState createState() => _ImagePickerState();
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ class SettingsIgnoreListView extends StatelessWidget {
 | 
			
		||||
    final client = Matrix.of(context).client;
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading: BackButton(),
 | 
			
		||||
        leading: const BackButton(),
 | 
			
		||||
        title: Text(L10n.of(context).ignoredUsers),
 | 
			
		||||
      ),
 | 
			
		||||
      body: MaxWidthBody(
 | 
			
		||||
@ -35,26 +35,26 @@ class SettingsIgnoreListView extends StatelessWidget {
 | 
			
		||||
                    textInputAction: TextInputAction.done,
 | 
			
		||||
                    onSubmitted: (_) => controller.ignoreUser(context),
 | 
			
		||||
                    decoration: InputDecoration(
 | 
			
		||||
                      border: OutlineInputBorder(),
 | 
			
		||||
                      border: const OutlineInputBorder(),
 | 
			
		||||
                      hintText: 'bad_guy:domain.abc',
 | 
			
		||||
                      prefixText: '@',
 | 
			
		||||
                      labelText: L10n.of(context).ignoreUsername,
 | 
			
		||||
                      suffixIcon: IconButton(
 | 
			
		||||
                        tooltip: L10n.of(context).ignore,
 | 
			
		||||
                        icon: Icon(Icons.done_outlined),
 | 
			
		||||
                        icon: const Icon(Icons.done_outlined),
 | 
			
		||||
                        onPressed: () => controller.ignoreUser(context),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                  SizedBox(height: 16),
 | 
			
		||||
                  const SizedBox(height: 16),
 | 
			
		||||
                  Text(
 | 
			
		||||
                    L10n.of(context).ignoreListDescription,
 | 
			
		||||
                    style: TextStyle(color: Colors.orange),
 | 
			
		||||
                    style: const TextStyle(color: Colors.orange),
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            Divider(height: 1),
 | 
			
		||||
            const Divider(height: 1),
 | 
			
		||||
            Expanded(
 | 
			
		||||
              child: StreamBuilder<Object>(
 | 
			
		||||
                  stream: client.onAccountData.stream
 | 
			
		||||
@ -74,7 +74,7 @@ class SettingsIgnoreListView extends StatelessWidget {
 | 
			
		||||
                              s.data?.displayName ?? client.ignoredUsers[i]),
 | 
			
		||||
                          trailing: IconButton(
 | 
			
		||||
                            tooltip: L10n.of(context).delete,
 | 
			
		||||
                            icon: Icon(Icons.delete_forever_outlined),
 | 
			
		||||
                            icon: const Icon(Icons.delete_forever_outlined),
 | 
			
		||||
                            onPressed: () => showFutureLoadingDialog(
 | 
			
		||||
                              context: context,
 | 
			
		||||
                              future: () =>
 | 
			
		||||
 | 
			
		||||
@ -8,14 +8,14 @@ import 'package:vrouter/vrouter.dart';
 | 
			
		||||
class MultipleEmotesSettingsView extends StatelessWidget {
 | 
			
		||||
  final MultipleEmotesSettingsController controller;
 | 
			
		||||
 | 
			
		||||
  MultipleEmotesSettingsView(this.controller, {Key key}) : super(key: key);
 | 
			
		||||
  const MultipleEmotesSettingsView(this.controller, {Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final room = Matrix.of(context).client.getRoomById(controller.roomId);
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading: BackButton(),
 | 
			
		||||
        leading: const BackButton(),
 | 
			
		||||
        title: Text(L10n.of(context).emotePacks),
 | 
			
		||||
      ),
 | 
			
		||||
      body: StreamBuilder(
 | 
			
		||||
 | 
			
		||||
@ -20,7 +20,7 @@ class SettingsNotificationsView extends StatelessWidget {
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading: BackButton(),
 | 
			
		||||
        leading: const BackButton(),
 | 
			
		||||
        title: Text(L10n.of(context).notifications),
 | 
			
		||||
      ),
 | 
			
		||||
      body: MaxWidthBody(
 | 
			
		||||
@ -56,11 +56,11 @@ class SettingsNotificationsView extends StatelessWidget {
 | 
			
		||||
                          backgroundColor:
 | 
			
		||||
                              Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                          foregroundColor: Colors.grey,
 | 
			
		||||
                          child: Icon(Icons.edit_outlined),
 | 
			
		||||
                          child: const Icon(Icons.edit_outlined),
 | 
			
		||||
                        ),
 | 
			
		||||
                        onTap: controller.openAndroidNotificationSettingsAction,
 | 
			
		||||
                      ),
 | 
			
		||||
                    Divider(thickness: 1),
 | 
			
		||||
                    const Divider(thickness: 1),
 | 
			
		||||
                    ListTile(
 | 
			
		||||
                      title: Text(
 | 
			
		||||
                        L10n.of(context).pushRules,
 | 
			
		||||
@ -78,7 +78,7 @@ class SettingsNotificationsView extends StatelessWidget {
 | 
			
		||||
                            controller.setNotificationSetting(item, enabled),
 | 
			
		||||
                      ),
 | 
			
		||||
                  },
 | 
			
		||||
                  Divider(thickness: 1),
 | 
			
		||||
                  const Divider(thickness: 1),
 | 
			
		||||
                  ListTile(
 | 
			
		||||
                    title: Text(
 | 
			
		||||
                      L10n.of(context).devices,
 | 
			
		||||
@ -99,13 +99,13 @@ class SettingsNotificationsView extends StatelessWidget {
 | 
			
		||||
                        );
 | 
			
		||||
                      }
 | 
			
		||||
                      if (snapshot.connectionState != ConnectionState.done) {
 | 
			
		||||
                        Center(
 | 
			
		||||
                        const Center(
 | 
			
		||||
                            child: CircularProgressIndicator.adaptive(
 | 
			
		||||
                                strokeWidth: 2));
 | 
			
		||||
                      }
 | 
			
		||||
                      final pushers = snapshot.data ?? [];
 | 
			
		||||
                      return ListView.builder(
 | 
			
		||||
                        physics: NeverScrollableScrollPhysics(),
 | 
			
		||||
                        physics: const NeverScrollableScrollPhysics(),
 | 
			
		||||
                        shrinkWrap: true,
 | 
			
		||||
                        itemCount: pushers.length,
 | 
			
		||||
                        itemBuilder: (_, i) => ListTile(
 | 
			
		||||
 | 
			
		||||
@ -22,27 +22,27 @@ class SettingsSecurityView extends StatelessWidget {
 | 
			
		||||
        child: Column(
 | 
			
		||||
          children: [
 | 
			
		||||
            ListTile(
 | 
			
		||||
              trailing: Icon(Icons.block_outlined),
 | 
			
		||||
              trailing: const Icon(Icons.block_outlined),
 | 
			
		||||
              title: Text(L10n.of(context).ignoredUsers),
 | 
			
		||||
              onTap: () => VRouter.of(context).to('ignorelist'),
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              trailing: Icon(Icons.security_outlined),
 | 
			
		||||
              trailing: const Icon(Icons.security_outlined),
 | 
			
		||||
              title: Text(
 | 
			
		||||
                L10n.of(context).changePassword,
 | 
			
		||||
              ),
 | 
			
		||||
              onTap: controller.changePasswordAccountAction,
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              trailing: Icon(Icons.email_outlined),
 | 
			
		||||
              trailing: const Icon(Icons.email_outlined),
 | 
			
		||||
              title: Text(L10n.of(context).passwordRecovery),
 | 
			
		||||
              onTap: () => VRouter.of(context).to('3pid'),
 | 
			
		||||
            ),
 | 
			
		||||
            if (Matrix.of(context).client.encryption != null) ...{
 | 
			
		||||
              Divider(thickness: 1),
 | 
			
		||||
              const Divider(thickness: 1),
 | 
			
		||||
              if (PlatformInfos.isMobile)
 | 
			
		||||
                ListTile(
 | 
			
		||||
                  trailing: Icon(Icons.lock_outlined),
 | 
			
		||||
                  trailing: const Icon(Icons.lock_outlined),
 | 
			
		||||
                  title: Text(L10n.of(context).appLock),
 | 
			
		||||
                  onTap: controller.setAppLockAction,
 | 
			
		||||
                ),
 | 
			
		||||
@ -55,11 +55,11 @@ class SettingsSecurityView extends StatelessWidget {
 | 
			
		||||
                  message: Matrix.of(context).client.fingerprintKey.beautified,
 | 
			
		||||
                  okLabel: L10n.of(context).ok,
 | 
			
		||||
                ),
 | 
			
		||||
                trailing: Icon(Icons.vpn_key_outlined),
 | 
			
		||||
                trailing: const Icon(Icons.vpn_key_outlined),
 | 
			
		||||
              ),
 | 
			
		||||
              ListTile(
 | 
			
		||||
                title: Text(L10n.of(context).cachedKeys),
 | 
			
		||||
                trailing: Icon(Icons.wb_cloudy_outlined),
 | 
			
		||||
                trailing: const Icon(Icons.wb_cloudy_outlined),
 | 
			
		||||
                subtitle: Text(
 | 
			
		||||
                    '${Matrix.of(context).client.encryption.keyManager.enabled ? L10n.of(context).onlineKeyBackupEnabled : L10n.of(context).onlineKeyBackupDisabled}\n${Matrix.of(context).client.encryption.crossSigning.enabled ? L10n.of(context).crossSigningEnabled : L10n.of(context).crossSigningDisabled}'),
 | 
			
		||||
                onTap: controller.bootstrapSettingsAction,
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@ class SettingsStyleView extends StatelessWidget {
 | 
			
		||||
    controller.currentTheme ??= AdaptiveTheme.of(context).mode;
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading: BackButton(),
 | 
			
		||||
        leading: const BackButton(),
 | 
			
		||||
        title: Text(L10n.of(context).changeTheme),
 | 
			
		||||
      ),
 | 
			
		||||
      body: MaxWidthBody(
 | 
			
		||||
@ -42,7 +42,7 @@ class SettingsStyleView extends StatelessWidget {
 | 
			
		||||
              title: Text(L10n.of(context).darkTheme),
 | 
			
		||||
              onChanged: controller.switchTheme,
 | 
			
		||||
            ),
 | 
			
		||||
            Divider(height: 1),
 | 
			
		||||
            const Divider(height: 1),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              title: Text(
 | 
			
		||||
                L10n.of(context).wallpaper,
 | 
			
		||||
@ -59,7 +59,7 @@ class SettingsStyleView extends StatelessWidget {
 | 
			
		||||
                  height: 38,
 | 
			
		||||
                  fit: BoxFit.cover,
 | 
			
		||||
                ),
 | 
			
		||||
                trailing: Icon(
 | 
			
		||||
                trailing: const Icon(
 | 
			
		||||
                  Icons.delete_forever_outlined,
 | 
			
		||||
                  color: Colors.red,
 | 
			
		||||
                ),
 | 
			
		||||
@ -68,11 +68,11 @@ class SettingsStyleView extends StatelessWidget {
 | 
			
		||||
            Builder(builder: (context) {
 | 
			
		||||
              return ListTile(
 | 
			
		||||
                title: Text(L10n.of(context).changeWallpaper),
 | 
			
		||||
                trailing: Icon(Icons.wallpaper_outlined),
 | 
			
		||||
                trailing: const Icon(Icons.wallpaper_outlined),
 | 
			
		||||
                onTap: controller.setWallpaperAction,
 | 
			
		||||
              );
 | 
			
		||||
            }),
 | 
			
		||||
            Divider(height: 1),
 | 
			
		||||
            const Divider(height: 1),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              title: Text(
 | 
			
		||||
                L10n.of(context).fontSize,
 | 
			
		||||
 | 
			
		||||
@ -38,38 +38,38 @@ class SettingsView extends StatelessWidget {
 | 
			
		||||
        body: ListView(
 | 
			
		||||
          children: <Widget>[
 | 
			
		||||
            ListTile(
 | 
			
		||||
              leading: Icon(Icons.notifications_outlined),
 | 
			
		||||
              leading: const Icon(Icons.notifications_outlined),
 | 
			
		||||
              title: Text(L10n.of(context).notifications),
 | 
			
		||||
              onTap: () => VRouter.of(context).to('/settings/notifications'),
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              leading: Icon(Icons.chat_bubble_outline),
 | 
			
		||||
              leading: const Icon(Icons.chat_bubble_outline),
 | 
			
		||||
              title: Text(L10n.of(context).chat),
 | 
			
		||||
              onTap: () => VRouter.of(context).to('/settings/chat'),
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              leading: Icon(Icons.account_box_outlined),
 | 
			
		||||
              leading: const Icon(Icons.account_box_outlined),
 | 
			
		||||
              title: Text(L10n.of(context).account),
 | 
			
		||||
              onTap: () => VRouter.of(context).to('/settings/account'),
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              leading: Icon(Icons.security_outlined),
 | 
			
		||||
              leading: const Icon(Icons.security_outlined),
 | 
			
		||||
              title: Text(L10n.of(context).security),
 | 
			
		||||
              onTap: () => VRouter.of(context).to('/settings/security'),
 | 
			
		||||
            ),
 | 
			
		||||
            Divider(thickness: 1),
 | 
			
		||||
            const Divider(thickness: 1),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              leading: Icon(Icons.help_outlined),
 | 
			
		||||
              leading: const Icon(Icons.help_outlined),
 | 
			
		||||
              title: Text(L10n.of(context).help),
 | 
			
		||||
              onTap: () => launch(AppConfig.supportUrl),
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              leading: Icon(Icons.privacy_tip_outlined),
 | 
			
		||||
              leading: const Icon(Icons.privacy_tip_outlined),
 | 
			
		||||
              title: Text(L10n.of(context).privacy),
 | 
			
		||||
              onTap: () => launch(AppConfig.privacyUrl),
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              leading: Icon(Icons.link_outlined),
 | 
			
		||||
              leading: const Icon(Icons.link_outlined),
 | 
			
		||||
              title: Text(L10n.of(context).about),
 | 
			
		||||
              onTap: () => PlatformInfos.showDialog(context),
 | 
			
		||||
            ),
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ class SignupPageView extends StatelessWidget {
 | 
			
		||||
                autofillHints:
 | 
			
		||||
                    controller.loading ? null : [AutofillHints.username],
 | 
			
		||||
                decoration: InputDecoration(
 | 
			
		||||
                    prefixIcon: Icon(Icons.account_box_outlined),
 | 
			
		||||
                    prefixIcon: const Icon(Icons.account_box_outlined),
 | 
			
		||||
                    hintText: L10n.of(context).username,
 | 
			
		||||
                    errorText: controller.usernameError,
 | 
			
		||||
                    labelText: L10n.of(context).username,
 | 
			
		||||
@ -41,7 +41,7 @@ class SignupPageView extends StatelessWidget {
 | 
			
		||||
                        ':${Matrix.of(context).getLoginClient().homeserver.host}'),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            Divider(),
 | 
			
		||||
            const Divider(),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              title: Text(L10n.of(context).chooseAStrongPassword),
 | 
			
		||||
              subtitle: Text(L10n.of(context).newPasswordDescription),
 | 
			
		||||
@ -57,7 +57,7 @@ class SignupPageView extends StatelessWidget {
 | 
			
		||||
                obscureText: !controller.showPassword,
 | 
			
		||||
                onSubmitted: controller.signup,
 | 
			
		||||
                decoration: InputDecoration(
 | 
			
		||||
                  prefixIcon: Icon(Icons.lock_outlined),
 | 
			
		||||
                  prefixIcon: const Icon(Icons.lock_outlined),
 | 
			
		||||
                  hintText: '****',
 | 
			
		||||
                  errorText: controller.passwordError,
 | 
			
		||||
                  suffixIcon: IconButton(
 | 
			
		||||
@ -71,16 +71,16 @@ class SignupPageView extends StatelessWidget {
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            Divider(),
 | 
			
		||||
            SizedBox(height: 12),
 | 
			
		||||
            const Divider(),
 | 
			
		||||
            const SizedBox(height: 12),
 | 
			
		||||
            Hero(
 | 
			
		||||
              tag: 'loginButton',
 | 
			
		||||
              child: Padding(
 | 
			
		||||
                padding: EdgeInsets.symmetric(horizontal: 12),
 | 
			
		||||
                padding: const EdgeInsets.symmetric(horizontal: 12),
 | 
			
		||||
                child: ElevatedButton(
 | 
			
		||||
                  onPressed: controller.loading ? null : controller.signup,
 | 
			
		||||
                  child: controller.loading
 | 
			
		||||
                      ? LinearProgressIndicator()
 | 
			
		||||
                      ? const LinearProgressIndicator()
 | 
			
		||||
                      : Text(L10n.of(context).signUp),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ class UserBottomSheetView extends StatelessWidget {
 | 
			
		||||
    final client = Matrix.of(context).client;
 | 
			
		||||
    final presence = client.presences[user.id];
 | 
			
		||||
    return Center(
 | 
			
		||||
      child: Container(
 | 
			
		||||
      child: SizedBox(
 | 
			
		||||
        width: min(
 | 
			
		||||
            MediaQuery.of(context).size.width, FluffyThemes.columnWidth * 1.5),
 | 
			
		||||
        child: Material(
 | 
			
		||||
@ -34,7 +34,7 @@ class UserBottomSheetView extends StatelessWidget {
 | 
			
		||||
                backgroundColor:
 | 
			
		||||
                    Theme.of(context).scaffoldBackgroundColor.withOpacity(0.5),
 | 
			
		||||
                leading: IconButton(
 | 
			
		||||
                  icon: Icon(Icons.arrow_downward_outlined),
 | 
			
		||||
                  icon: const Icon(Icons.arrow_downward_outlined),
 | 
			
		||||
                  onPressed: Navigator.of(context, rootNavigator: false).pop,
 | 
			
		||||
                  tooltip: L10n.of(context).close,
 | 
			
		||||
                ),
 | 
			
		||||
@ -109,7 +109,7 @@ class UserBottomSheetView extends StatelessWidget {
 | 
			
		||||
                  ListTile(
 | 
			
		||||
                    title: Text(L10n.of(context).username),
 | 
			
		||||
                    subtitle: Text(user.id),
 | 
			
		||||
                    trailing: Icon(Icons.share_outlined),
 | 
			
		||||
                    trailing: const Icon(Icons.share_outlined),
 | 
			
		||||
                    onTap: () => FluffyShare.share(
 | 
			
		||||
                        user.id, controller.widget.outerContext),
 | 
			
		||||
                  ),
 | 
			
		||||
@ -149,7 +149,7 @@ class _TextWithIcon extends StatelessWidget {
 | 
			
		||||
      mainAxisSize: MainAxisSize.min,
 | 
			
		||||
      children: [
 | 
			
		||||
        Icon(iconData),
 | 
			
		||||
        SizedBox(width: 16),
 | 
			
		||||
        const SizedBox(width: 16),
 | 
			
		||||
        Text(text),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@ -16,21 +16,21 @@ class VideoViewerView extends StatelessWidget {
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        elevation: 0,
 | 
			
		||||
        leading: IconButton(
 | 
			
		||||
          icon: Icon(Icons.close),
 | 
			
		||||
          icon: const Icon(Icons.close),
 | 
			
		||||
          onPressed: Navigator.of(context).pop,
 | 
			
		||||
          color: Colors.white,
 | 
			
		||||
          tooltip: L10n.of(context).close,
 | 
			
		||||
        ),
 | 
			
		||||
        backgroundColor: Color(0x44000000),
 | 
			
		||||
        backgroundColor: const Color(0x44000000),
 | 
			
		||||
        actions: [
 | 
			
		||||
          IconButton(
 | 
			
		||||
            icon: Icon(Icons.reply_outlined),
 | 
			
		||||
            icon: const Icon(Icons.reply_outlined),
 | 
			
		||||
            onPressed: controller.forwardAction,
 | 
			
		||||
            color: Colors.white,
 | 
			
		||||
            tooltip: L10n.of(context).share,
 | 
			
		||||
          ),
 | 
			
		||||
          IconButton(
 | 
			
		||||
            icon: Icon(Icons.download_outlined),
 | 
			
		||||
            icon: const Icon(Icons.download_outlined),
 | 
			
		||||
            onPressed: controller.saveFileAction,
 | 
			
		||||
            color: Colors.white,
 | 
			
		||||
            tooltip: L10n.of(context).downloadFile,
 | 
			
		||||
@ -41,7 +41,7 @@ class VideoViewerView extends StatelessWidget {
 | 
			
		||||
        child: controller.error != null
 | 
			
		||||
            ? Text(controller.error.toString())
 | 
			
		||||
            : (controller.chewieController == null
 | 
			
		||||
                ? CircularProgressIndicator.adaptive(strokeWidth: 2)
 | 
			
		||||
                ? const CircularProgressIndicator.adaptive(strokeWidth: 2)
 | 
			
		||||
                : Chewie(
 | 
			
		||||
                    controller: controller.chewieController,
 | 
			
		||||
                  )),
 | 
			
		||||
 | 
			
		||||
@ -97,7 +97,9 @@ class BackgroundPush {
 | 
			
		||||
      {final void Function(String errorMsg, {Uri link}) onFcmError}) {
 | 
			
		||||
    final instance = BackgroundPush.clientOnly(_client);
 | 
			
		||||
    instance.context = _context;
 | 
			
		||||
    // ignore: prefer_initializing_formals
 | 
			
		||||
    instance.router = router;
 | 
			
		||||
    // ignore: prefer_initializing_formals
 | 
			
		||||
    instance.onFcmError = onFcmError;
 | 
			
		||||
    instance.fullInit();
 | 
			
		||||
    return instance;
 | 
			
		||||
@ -301,7 +303,7 @@ class BackgroundPush {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // initialise the plugin. app_icon needs to be a added as a drawable resource to the Android head project
 | 
			
		||||
    final initializationSettingsAndroid =
 | 
			
		||||
    const initializationSettingsAndroid =
 | 
			
		||||
        AndroidInitializationSettings('notifications_icon');
 | 
			
		||||
    final initializationSettingsIOS =
 | 
			
		||||
        IOSInitializationSettings(onDidReceiveLocalNotification: (i, a, b, c) {
 | 
			
		||||
@ -596,11 +598,6 @@ class BackgroundPush {
 | 
			
		||||
    // load the locale
 | 
			
		||||
    await loadLocale();
 | 
			
		||||
 | 
			
		||||
    // Count all unread events
 | 
			
		||||
    var unreadEvents = 0;
 | 
			
		||||
    client.rooms
 | 
			
		||||
        .forEach((Room room) => unreadEvents += room.notificationCount ?? 0);
 | 
			
		||||
 | 
			
		||||
    // Calculate title
 | 
			
		||||
    final title = l10n.unreadMessages(room.notificationCount ?? 0);
 | 
			
		||||
 | 
			
		||||
@ -645,7 +642,7 @@ class BackgroundPush {
 | 
			
		||||
      ),
 | 
			
		||||
      ticker: l10n.newMessageInFluffyChat,
 | 
			
		||||
    );
 | 
			
		||||
    final iOSPlatformChannelSpecifics = IOSNotificationDetails();
 | 
			
		||||
    const iOSPlatformChannelSpecifics = IOSNotificationDetails();
 | 
			
		||||
    final platformChannelSpecifics = NotificationDetails(
 | 
			
		||||
      android: androidPlatformChannelSpecifics,
 | 
			
		||||
      iOS: iOSPlatformChannelSpecifics,
 | 
			
		||||
@ -681,7 +678,7 @@ class BackgroundPush {
 | 
			
		||||
 | 
			
		||||
      // Display notification
 | 
			
		||||
      final androidPlatformChannelSpecifics = _getAndroidNotificationDetails();
 | 
			
		||||
      final iOSPlatformChannelSpecifics = IOSNotificationDetails();
 | 
			
		||||
      const iOSPlatformChannelSpecifics = IOSNotificationDetails();
 | 
			
		||||
      final platformChannelSpecifics = NotificationDetails(
 | 
			
		||||
        android: androidPlatformChannelSpecifics,
 | 
			
		||||
        iOS: iOSPlatformChannelSpecifics,
 | 
			
		||||
@ -702,7 +699,7 @@ class BackgroundPush {
 | 
			
		||||
  AndroidNotificationDetails _getAndroidNotificationDetails(
 | 
			
		||||
      {MessagingStyleInformation styleInformation, String ticker}) {
 | 
			
		||||
    final color = (context != null ? Theme.of(context).primaryColor : null) ??
 | 
			
		||||
        Color(0xFF5625BA);
 | 
			
		||||
        const Color(0xFF5625BA);
 | 
			
		||||
 | 
			
		||||
    return AndroidNotificationDetails(
 | 
			
		||||
      AppConfig.pushNotificationsChannelId,
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ extension DateTimeExtension on DateTime {
 | 
			
		||||
  /// Two message events can belong to the same environment. That means that they
 | 
			
		||||
  /// don't need to display the time they were sent because they are close
 | 
			
		||||
  /// enaugh.
 | 
			
		||||
  static final minutesBetweenEnvironments = 5;
 | 
			
		||||
  static const minutesBetweenEnvironments = 5;
 | 
			
		||||
 | 
			
		||||
  /// Checks if two DateTimes are close enough to belong to the same
 | 
			
		||||
  /// environment.
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,7 @@ class Store {
 | 
			
		||||
  static final _mutex = AsyncMutex();
 | 
			
		||||
 | 
			
		||||
  Store()
 | 
			
		||||
      : secureStorage = PlatformInfos.isMobile ? FlutterSecureStorage() : null;
 | 
			
		||||
      : secureStorage = PlatformInfos.isMobile ? const FlutterSecureStorage() : null;
 | 
			
		||||
 | 
			
		||||
  Future<void> _setupLocalStorage() async {
 | 
			
		||||
    if (storage == null) {
 | 
			
		||||
 | 
			
		||||
@ -55,7 +55,7 @@ class FlutterMatrixHiveStore extends FamedlySdkHiveDatabase {
 | 
			
		||||
      // Workaround for secure storage is calling Platform.operatingSystem on web
 | 
			
		||||
      if (kIsWeb) throw MissingPluginException();
 | 
			
		||||
 | 
			
		||||
      final secureStorage = const FlutterSecureStorage();
 | 
			
		||||
      const secureStorage = FlutterSecureStorage();
 | 
			
		||||
      final containsEncryptionKey =
 | 
			
		||||
          await secureStorage.containsKey(key: _hiveCipherStorageKey);
 | 
			
		||||
      if (!containsEncryptionKey) {
 | 
			
		||||
 | 
			
		||||
@ -53,11 +53,11 @@ abstract class PlatformInfos {
 | 
			
		||||
        ),
 | 
			
		||||
        OutlinedButton(
 | 
			
		||||
          onPressed: () => launch(AppConfig.emojiFontUrl),
 | 
			
		||||
          child: Text(AppConfig.emojiFontName),
 | 
			
		||||
          child: const Text(AppConfig.emojiFontName),
 | 
			
		||||
        ),
 | 
			
		||||
        OutlinedButton(
 | 
			
		||||
          onPressed: () => VRouter.of(context).to('logs'),
 | 
			
		||||
          child: Text('Logs'),
 | 
			
		||||
          child: const Text('Logs'),
 | 
			
		||||
        ),
 | 
			
		||||
        SentrySwitchListTile(label: L10n.of(context).sendBugReports),
 | 
			
		||||
      ],
 | 
			
		||||
 | 
			
		||||
@ -45,8 +45,8 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
 | 
			
		||||
              value: 'mute',
 | 
			
		||||
              child: Row(
 | 
			
		||||
                children: [
 | 
			
		||||
                  Icon(Icons.notifications_off_outlined),
 | 
			
		||||
                  SizedBox(width: 12),
 | 
			
		||||
                  const Icon(Icons.notifications_off_outlined),
 | 
			
		||||
                  const SizedBox(width: 12),
 | 
			
		||||
                  Text(L10n.of(context).muteChat),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
@ -55,8 +55,8 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
 | 
			
		||||
              value: 'unmute',
 | 
			
		||||
              child: Row(
 | 
			
		||||
                children: [
 | 
			
		||||
                  Icon(Icons.notifications_on_outlined),
 | 
			
		||||
                  SizedBox(width: 12),
 | 
			
		||||
                  const Icon(Icons.notifications_on_outlined),
 | 
			
		||||
                  const SizedBox(width: 12),
 | 
			
		||||
                  Text(L10n.of(context).unmuteChat),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
@ -65,8 +65,8 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
 | 
			
		||||
        value: 'leave',
 | 
			
		||||
        child: Row(
 | 
			
		||||
          children: [
 | 
			
		||||
            Icon(Icons.delete_outlined),
 | 
			
		||||
            SizedBox(width: 12),
 | 
			
		||||
            const Icon(Icons.delete_outlined),
 | 
			
		||||
            const SizedBox(width: 12),
 | 
			
		||||
            Text(L10n.of(context).leave),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
@ -79,8 +79,8 @@ class _ChatSettingsPopupMenuState extends State<ChatSettingsPopupMenu> {
 | 
			
		||||
          value: 'details',
 | 
			
		||||
          child: Row(
 | 
			
		||||
            children: [
 | 
			
		||||
              Icon(Icons.info_outline_rounded),
 | 
			
		||||
              SizedBox(width: 12),
 | 
			
		||||
              const Icon(Icons.info_outline_rounded),
 | 
			
		||||
              const SizedBox(width: 12),
 | 
			
		||||
              Text(L10n.of(context).chatDetails),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,8 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
 | 
			
		||||
import 'matrix.dart';
 | 
			
		||||
 | 
			
		||||
class ConnectionStatusHeader extends StatefulWidget {
 | 
			
		||||
  const ConnectionStatusHeader({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  _ConnectionStatusHeaderState createState() => _ConnectionStatusHeaderState();
 | 
			
		||||
}
 | 
			
		||||
@ -19,7 +21,8 @@ class _ConnectionStatusHeaderState extends State<ConnectionStatusHeader> {
 | 
			
		||||
          _lastSyncReceived.millisecondsSinceEpoch <
 | 
			
		||||
      (Matrix.of(context).client.sendMessageTimeoutSeconds + 2) * 1000;
 | 
			
		||||
  static DateTime _lastSyncReceived = DateTime(0);
 | 
			
		||||
  SyncStatusUpdate _status = SyncStatusUpdate(SyncStatus.waitingForResponse);
 | 
			
		||||
  SyncStatusUpdate _status =
 | 
			
		||||
      const SyncStatusUpdate(SyncStatus.waitingForResponse);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void dispose() {
 | 
			
		||||
@ -44,12 +47,12 @@ class _ConnectionStatusHeaderState extends State<ConnectionStatusHeader> {
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
    return AnimatedContainer(
 | 
			
		||||
      duration: Duration(milliseconds: 200),
 | 
			
		||||
      duration: const Duration(milliseconds: 200),
 | 
			
		||||
      curve: Curves.bounceInOut,
 | 
			
		||||
      height: _connected ? 0 : 36,
 | 
			
		||||
      clipBehavior: Clip.hardEdge,
 | 
			
		||||
      decoration: BoxDecoration(color: Theme.of(context).secondaryHeaderColor),
 | 
			
		||||
      padding: EdgeInsets.symmetric(horizontal: 12),
 | 
			
		||||
      padding: const EdgeInsets.symmetric(horizontal: 12),
 | 
			
		||||
      child: Row(
 | 
			
		||||
        mainAxisAlignment: MainAxisAlignment.center,
 | 
			
		||||
        children: [
 | 
			
		||||
@ -61,7 +64,7 @@ class _ConnectionStatusHeaderState extends State<ConnectionStatusHeader> {
 | 
			
		||||
              value: _connected ? 1.0 : _status.progress,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          SizedBox(width: 12),
 | 
			
		||||
          const SizedBox(width: 12),
 | 
			
		||||
          Text(
 | 
			
		||||
            _status.toLocalizedString(context),
 | 
			
		||||
            maxLines: 1,
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,7 @@ class _ContactsState extends State<ContactsList> {
 | 
			
		||||
              _lastSetState.millisecondsSinceEpoch <
 | 
			
		||||
          1000) {
 | 
			
		||||
        _coolDown?.cancel();
 | 
			
		||||
        _coolDown = Timer(Duration(seconds: 1), _updateView);
 | 
			
		||||
        _coolDown = Timer(const Duration(seconds: 1), _updateView);
 | 
			
		||||
      } else {
 | 
			
		||||
        _updateView();
 | 
			
		||||
      }
 | 
			
		||||
@ -77,7 +77,7 @@ class _ContactListTile extends StatelessWidget {
 | 
			
		||||
              snapshot.data?.displayName ?? contact.senderId.localpart;
 | 
			
		||||
          final avatarUrl = snapshot.data?.avatarUrl;
 | 
			
		||||
          return ListTile(
 | 
			
		||||
            leading: Container(
 | 
			
		||||
            leading: SizedBox(
 | 
			
		||||
              width: Avatar.defaultSize,
 | 
			
		||||
              height: Avatar.defaultSize,
 | 
			
		||||
              child: Stack(
 | 
			
		||||
 | 
			
		||||
@ -63,14 +63,14 @@ class ContentBanner extends StatelessWidget {
 | 
			
		||||
          ),
 | 
			
		||||
          if (onEdit != null)
 | 
			
		||||
            Container(
 | 
			
		||||
              margin: EdgeInsets.all(8),
 | 
			
		||||
              margin: const EdgeInsets.all(8),
 | 
			
		||||
              alignment: Alignment.bottomRight,
 | 
			
		||||
              child: FloatingActionButton(
 | 
			
		||||
                mini: true,
 | 
			
		||||
                onPressed: onEdit,
 | 
			
		||||
                backgroundColor: Theme.of(context).backgroundColor,
 | 
			
		||||
                foregroundColor: Theme.of(context).textTheme.bodyText1.color,
 | 
			
		||||
                child: Icon(Icons.camera_alt_outlined),
 | 
			
		||||
                child: const Icon(Icons.camera_alt_outlined),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
        ],
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ class DefaultAppBarSearchField extends StatefulWidget {
 | 
			
		||||
  final bool unfocusOnClear;
 | 
			
		||||
  final bool autocorrect;
 | 
			
		||||
 | 
			
		||||
  DefaultAppBarSearchField({
 | 
			
		||||
  const DefaultAppBarSearchField({
 | 
			
		||||
    Key key,
 | 
			
		||||
    this.searchController,
 | 
			
		||||
    this.onChanged,
 | 
			
		||||
@ -78,7 +78,7 @@ class DefaultAppBarSearchFieldState extends State<DefaultAppBarSearchField> {
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Container(
 | 
			
		||||
      height: 40,
 | 
			
		||||
      padding: widget.padding ?? EdgeInsets.only(right: 12),
 | 
			
		||||
      padding: widget.padding ?? const EdgeInsets.only(right: 12),
 | 
			
		||||
      child: TextField(
 | 
			
		||||
        autofocus: widget.autofocus,
 | 
			
		||||
        autocorrect: widget.autocorrect,
 | 
			
		||||
@ -97,7 +97,7 @@ class DefaultAppBarSearchFieldState extends State<DefaultAppBarSearchField> {
 | 
			
		||||
            borderSide:
 | 
			
		||||
                BorderSide(color: Theme.of(context).secondaryHeaderColor),
 | 
			
		||||
          ),
 | 
			
		||||
          contentPadding: EdgeInsets.only(
 | 
			
		||||
          contentPadding: const EdgeInsets.only(
 | 
			
		||||
            top: 8,
 | 
			
		||||
            bottom: 8,
 | 
			
		||||
            left: 16,
 | 
			
		||||
@ -110,7 +110,7 @@ class DefaultAppBarSearchFieldState extends State<DefaultAppBarSearchField> {
 | 
			
		||||
                          (_searchController.text?.isNotEmpty ?? false)))
 | 
			
		||||
              ? IconButton(
 | 
			
		||||
                  tooltip: L10n.of(context).clearText,
 | 
			
		||||
                  icon: Icon(Icons.backspace_outlined),
 | 
			
		||||
                  icon: const Icon(Icons.backspace_outlined),
 | 
			
		||||
                  onPressed: () {
 | 
			
		||||
                    _searchController.clear();
 | 
			
		||||
                    widget.onChanged?.call('');
 | 
			
		||||
 | 
			
		||||
@ -132,10 +132,10 @@ class _AudioPlayerState extends State<AudioPlayerWidget> {
 | 
			
		||||
    return Row(
 | 
			
		||||
      mainAxisSize: MainAxisSize.min,
 | 
			
		||||
      children: <Widget>[
 | 
			
		||||
        Container(
 | 
			
		||||
        SizedBox(
 | 
			
		||||
          width: 30,
 | 
			
		||||
          child: status == AudioPlayerStatus.downloading
 | 
			
		||||
              ? CircularProgressIndicator.adaptive(strokeWidth: 2)
 | 
			
		||||
              ? const CircularProgressIndicator.adaptive(strokeWidth: 2)
 | 
			
		||||
              : IconButton(
 | 
			
		||||
                  icon: Icon(
 | 
			
		||||
                    audioPlayer.state == PlayerState.PLAYING
 | 
			
		||||
@ -172,7 +172,7 @@ class _AudioPlayerState extends State<AudioPlayerWidget> {
 | 
			
		||||
            color: widget.color,
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
        SizedBox(width: 8),
 | 
			
		||||
        const SizedBox(width: 8),
 | 
			
		||||
        IconButton(
 | 
			
		||||
          icon: Icon(
 | 
			
		||||
            Icons.download_outlined,
 | 
			
		||||
 | 
			
		||||
@ -18,13 +18,15 @@ class HtmlMessage extends StatelessWidget {
 | 
			
		||||
  final TextStyle linkStyle;
 | 
			
		||||
  final double emoteSize;
 | 
			
		||||
 | 
			
		||||
  const HtmlMessage(
 | 
			
		||||
      {this.html,
 | 
			
		||||
      this.maxLines,
 | 
			
		||||
      this.room,
 | 
			
		||||
      this.defaultTextStyle,
 | 
			
		||||
      this.linkStyle,
 | 
			
		||||
      this.emoteSize});
 | 
			
		||||
  const HtmlMessage({
 | 
			
		||||
    Key key,
 | 
			
		||||
    this.html,
 | 
			
		||||
    this.maxLines,
 | 
			
		||||
    this.room,
 | 
			
		||||
    this.defaultTextStyle,
 | 
			
		||||
    this.linkStyle,
 | 
			
		||||
    this.emoteSize,
 | 
			
		||||
  }) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
@ -35,7 +37,7 @@ class HtmlMessage extends StatelessWidget {
 | 
			
		||||
    // miss-matching tags, and this way we actually correctly identify what we want to strip and, well,
 | 
			
		||||
    // strip it.
 | 
			
		||||
    final renderHtml = html.replaceAll(
 | 
			
		||||
        RegExp('<mx-reply>.*<\/mx-reply>',
 | 
			
		||||
        RegExp('<mx-reply>.*</mx-reply>',
 | 
			
		||||
            caseSensitive: false, multiLine: false, dotAll: true),
 | 
			
		||||
        '');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -205,8 +205,8 @@ class _ImageBubbleState extends State<ImageBubble> {
 | 
			
		||||
            child: Row(
 | 
			
		||||
              mainAxisSize: MainAxisSize.min,
 | 
			
		||||
              children: [
 | 
			
		||||
                Icon(Icons.download_outlined),
 | 
			
		||||
                SizedBox(width: 8),
 | 
			
		||||
                const Icon(Icons.download_outlined),
 | 
			
		||||
                const SizedBox(width: 8),
 | 
			
		||||
                Text(
 | 
			
		||||
                  filename,
 | 
			
		||||
                  overflow: TextOverflow.fade,
 | 
			
		||||
@ -217,7 +217,7 @@ class _ImageBubbleState extends State<ImageBubble> {
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          if (widget.event.sizeString != null) Text(widget.event.sizeString),
 | 
			
		||||
          SizedBox(height: 8),
 | 
			
		||||
          const SizedBox(height: 8),
 | 
			
		||||
          Text((error ?? _error).toString()),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
@ -249,7 +249,7 @@ class _ImageBubbleState extends State<ImageBubble> {
 | 
			
		||||
      children: <Widget>[
 | 
			
		||||
        if (blurhash != null) blurhash,
 | 
			
		||||
        Center(
 | 
			
		||||
          child: child ?? CircularProgressIndicator.adaptive(strokeWidth: 2),
 | 
			
		||||
          child: child ?? const CircularProgressIndicator.adaptive(strokeWidth: 2),
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
@ -412,7 +412,7 @@ class _ImageBubbleState extends State<ImageBubble> {
 | 
			
		||||
        child: Hero(
 | 
			
		||||
          tag: widget.event.eventId,
 | 
			
		||||
          child: AnimatedSwitcher(
 | 
			
		||||
            duration: Duration(milliseconds: 1000),
 | 
			
		||||
            duration: const Duration(milliseconds: 1000),
 | 
			
		||||
            child: Container(
 | 
			
		||||
              key: ValueKey(key),
 | 
			
		||||
              constraints: widget.maxSize
 | 
			
		||||
 | 
			
		||||
@ -42,7 +42,7 @@ class MapBubble extends StatelessWidget {
 | 
			
		||||
                markers: [
 | 
			
		||||
                  Marker(
 | 
			
		||||
                    point: LatLng(latitude, longitude),
 | 
			
		||||
                    builder: (context) => Icon(
 | 
			
		||||
                    builder: (context) => const Icon(
 | 
			
		||||
                      Icons.location_pin,
 | 
			
		||||
                      color: Colors.red,
 | 
			
		||||
                    ),
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,9 @@ class Message extends StatelessWidget {
 | 
			
		||||
      this.scrollToEventId,
 | 
			
		||||
      @required this.unfold,
 | 
			
		||||
      this.selected,
 | 
			
		||||
      this.timeline});
 | 
			
		||||
      this.timeline,
 | 
			
		||||
      Key key})
 | 
			
		||||
      : super(key: key);
 | 
			
		||||
 | 
			
		||||
  /// Indicates wheither the user may use a mouse instead
 | 
			
		||||
  /// of touchscreen.
 | 
			
		||||
@ -99,8 +101,8 @@ class Message extends StatelessWidget {
 | 
			
		||||
                ),
 | 
			
		||||
                padding:
 | 
			
		||||
                    const EdgeInsets.symmetric(vertical: 6, horizontal: 10),
 | 
			
		||||
                constraints:
 | 
			
		||||
                    BoxConstraints(maxWidth: FluffyThemes.columnWidth * 1.5),
 | 
			
		||||
                constraints: const BoxConstraints(
 | 
			
		||||
                    maxWidth: FluffyThemes.columnWidth * 1.5),
 | 
			
		||||
                child: Stack(
 | 
			
		||||
                  children: <Widget>[
 | 
			
		||||
                    Column(
 | 
			
		||||
@ -134,7 +136,8 @@ class Message extends StatelessWidget {
 | 
			
		||||
                                },
 | 
			
		||||
                                child: AbsorbPointer(
 | 
			
		||||
                                  child: Container(
 | 
			
		||||
                                    margin: EdgeInsets.symmetric(vertical: 4.0),
 | 
			
		||||
                                    margin: const EdgeInsets.symmetric(
 | 
			
		||||
                                        vertical: 4.0),
 | 
			
		||||
                                    child: ReplyContent(replyEvent,
 | 
			
		||||
                                        lightText: ownMessage,
 | 
			
		||||
                                        timeline: timeline),
 | 
			
		||||
@ -147,7 +150,7 @@ class Message extends StatelessWidget {
 | 
			
		||||
                          displayEvent,
 | 
			
		||||
                          textColor: textColor,
 | 
			
		||||
                        ),
 | 
			
		||||
                        SizedBox(height: 3),
 | 
			
		||||
                        const SizedBox(height: 3),
 | 
			
		||||
                        Opacity(
 | 
			
		||||
                          opacity: 0,
 | 
			
		||||
                          child: _MetaRow(
 | 
			
		||||
@ -181,7 +184,7 @@ class Message extends StatelessWidget {
 | 
			
		||||
      ),
 | 
			
		||||
    ];
 | 
			
		||||
    final avatarOrSizedBox = sameSender
 | 
			
		||||
        ? SizedBox(width: Avatar.defaultSize)
 | 
			
		||||
        ? const SizedBox(width: Avatar.defaultSize)
 | 
			
		||||
        : Avatar(
 | 
			
		||||
            event.sender.avatarUrl,
 | 
			
		||||
            event.sender.calcDisplayname(),
 | 
			
		||||
@ -224,10 +227,11 @@ class Message extends StatelessWidget {
 | 
			
		||||
        color: selected
 | 
			
		||||
            ? Theme.of(context).primaryColor.withAlpha(100)
 | 
			
		||||
            : Theme.of(context).primaryColor.withAlpha(0),
 | 
			
		||||
        constraints: BoxConstraints(maxWidth: FluffyThemes.columnWidth * 2.5),
 | 
			
		||||
        constraints:
 | 
			
		||||
            const BoxConstraints(maxWidth: FluffyThemes.columnWidth * 2.5),
 | 
			
		||||
        child: Padding(
 | 
			
		||||
          padding:
 | 
			
		||||
              EdgeInsets.only(left: 8.0, right: 8.0, bottom: 4.0, top: 4.0),
 | 
			
		||||
          padding: const EdgeInsets.only(
 | 
			
		||||
              left: 8.0, right: 8.0, bottom: 4.0, top: 4.0),
 | 
			
		||||
          child: container,
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
@ -267,7 +271,7 @@ class _MetaRow extends StatelessWidget {
 | 
			
		||||
                  .withAlpha(200),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        if (showDisplayname) SizedBox(width: 4),
 | 
			
		||||
        if (showDisplayname) const SizedBox(width: 4),
 | 
			
		||||
        Text(
 | 
			
		||||
          event.originServerTs.localizedTime(context),
 | 
			
		||||
          style: TextStyle(
 | 
			
		||||
@ -284,7 +288,7 @@ class _MetaRow extends StatelessWidget {
 | 
			
		||||
              color: color,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        if (ownMessage) SizedBox(width: 2),
 | 
			
		||||
        if (ownMessage) const SizedBox(width: 2),
 | 
			
		||||
        if (ownMessage)
 | 
			
		||||
          Icon(
 | 
			
		||||
            displayEvent.statusIcon,
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@ class MessageContent extends StatelessWidget {
 | 
			
		||||
  final Event event;
 | 
			
		||||
  final Color textColor;
 | 
			
		||||
 | 
			
		||||
  const MessageContent(this.event, {this.textColor});
 | 
			
		||||
  const MessageContent(this.event, {Key key,this.textColor}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  void _verifyOrRequestKey(BuildContext context) async {
 | 
			
		||||
    if (event.content['can_request_session'] != true) {
 | 
			
		||||
@ -50,7 +50,7 @@ class MessageContent extends StatelessWidget {
 | 
			
		||||
            if (await client.encryption.keyManager.isCached()) {
 | 
			
		||||
              break;
 | 
			
		||||
            }
 | 
			
		||||
            await Future.delayed(Duration(seconds: 1));
 | 
			
		||||
            await Future.delayed(const Duration(seconds: 1));
 | 
			
		||||
          }
 | 
			
		||||
          final timeline = await event.room.getTimeline();
 | 
			
		||||
          timeline.requestKeys();
 | 
			
		||||
@ -121,7 +121,7 @@ class MessageContent extends StatelessWidget {
 | 
			
		||||
                      fit: BoxFit.cover,
 | 
			
		||||
                      tapToView: false,
 | 
			
		||||
                    ),
 | 
			
		||||
                    Icon(Icons.play_circle_outline,
 | 
			
		||||
                    const Icon(Icons.play_circle_outline,
 | 
			
		||||
                        size: 200, color: Colors.grey),
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
@ -169,7 +169,7 @@ class MessageContent extends StatelessWidget {
 | 
			
		||||
                onPrimary: Theme.of(context).textTheme.bodyText1.color,
 | 
			
		||||
              ),
 | 
			
		||||
              onPressed: () => _verifyOrRequestKey(context),
 | 
			
		||||
              icon: Icon(Icons.lock_outline),
 | 
			
		||||
              icon: const Icon(Icons.lock_outline),
 | 
			
		||||
              label: Text(L10n.of(context).encrypted),
 | 
			
		||||
            );
 | 
			
		||||
          case MessageTypes.Location:
 | 
			
		||||
@ -194,7 +194,7 @@ class MessageContent extends StatelessWidget {
 | 
			
		||||
                      latitude: latlong.first,
 | 
			
		||||
                      longitude: latlong.last,
 | 
			
		||||
                    ),
 | 
			
		||||
                    SizedBox(height: 6),
 | 
			
		||||
                    const SizedBox(height: 6),
 | 
			
		||||
                    OutlinedButton.icon(
 | 
			
		||||
                      icon: Icon(Icons.location_on_outlined, color: textColor),
 | 
			
		||||
                      onPressed:
 | 
			
		||||
@ -219,7 +219,7 @@ class MessageContent extends StatelessWidget {
 | 
			
		||||
                  onPrimary: Theme.of(context).textTheme.bodyText1.color,
 | 
			
		||||
                ),
 | 
			
		||||
                onPressed: () => launch(event.body),
 | 
			
		||||
                icon: Icon(Icons.phone_outlined, color: Colors.green),
 | 
			
		||||
                icon: const Icon(Icons.phone_outlined, color: Colors.green),
 | 
			
		||||
                label: Text(L10n.of(context).videoCall),
 | 
			
		||||
              );
 | 
			
		||||
            }
 | 
			
		||||
@ -228,7 +228,7 @@ class MessageContent extends StatelessWidget {
 | 
			
		||||
                mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                children: [
 | 
			
		||||
                  Icon(Icons.delete_forever_outlined, color: textColor),
 | 
			
		||||
                  SizedBox(width: 4),
 | 
			
		||||
                  const SizedBox(width: 4),
 | 
			
		||||
                  Text(
 | 
			
		||||
                    event.getLocalizedBody(MatrixLocals(L10n.of(context)),
 | 
			
		||||
                        hideReply: true),
 | 
			
		||||
 | 
			
		||||
@ -14,42 +14,40 @@ class MessageDownloadContent extends StatelessWidget {
 | 
			
		||||
    final String filename = event.content.containsKey('filename')
 | 
			
		||||
        ? event.content['filename']
 | 
			
		||||
        : event.body;
 | 
			
		||||
    return Container(
 | 
			
		||||
      child: Column(
 | 
			
		||||
        crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
        mainAxisSize: MainAxisSize.min,
 | 
			
		||||
        children: <Widget>[
 | 
			
		||||
          ElevatedButton(
 | 
			
		||||
            style: ElevatedButton.styleFrom(
 | 
			
		||||
              primary: Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
              onPrimary: Theme.of(context).textTheme.bodyText1.color,
 | 
			
		||||
            ),
 | 
			
		||||
            onPressed: () => event.saveFile(context),
 | 
			
		||||
            child: Row(
 | 
			
		||||
              mainAxisSize: MainAxisSize.min,
 | 
			
		||||
              children: [
 | 
			
		||||
                Icon(Icons.download_outlined),
 | 
			
		||||
                SizedBox(width: 8),
 | 
			
		||||
                Expanded(
 | 
			
		||||
                  child: Text(
 | 
			
		||||
                    filename,
 | 
			
		||||
                    overflow: TextOverflow.fade,
 | 
			
		||||
                    softWrap: false,
 | 
			
		||||
                    maxLines: 1,
 | 
			
		||||
                  ),
 | 
			
		||||
    return Column(
 | 
			
		||||
      crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
      mainAxisSize: MainAxisSize.min,
 | 
			
		||||
      children: <Widget>[
 | 
			
		||||
        ElevatedButton(
 | 
			
		||||
          style: ElevatedButton.styleFrom(
 | 
			
		||||
            primary: Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
            onPrimary: Theme.of(context).textTheme.bodyText1.color,
 | 
			
		||||
          ),
 | 
			
		||||
          onPressed: () => event.saveFile(context),
 | 
			
		||||
          child: Row(
 | 
			
		||||
            mainAxisSize: MainAxisSize.min,
 | 
			
		||||
            children: [
 | 
			
		||||
              const Icon(Icons.download_outlined),
 | 
			
		||||
              const SizedBox(width: 8),
 | 
			
		||||
              Expanded(
 | 
			
		||||
                child: Text(
 | 
			
		||||
                  filename,
 | 
			
		||||
                  overflow: TextOverflow.fade,
 | 
			
		||||
                  softWrap: false,
 | 
			
		||||
                  maxLines: 1,
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
        if (event.sizeString != null)
 | 
			
		||||
          Text(
 | 
			
		||||
            event.sizeString,
 | 
			
		||||
            style: TextStyle(
 | 
			
		||||
              color: textColor,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          if (event.sizeString != null)
 | 
			
		||||
            Text(
 | 
			
		||||
              event.sizeString,
 | 
			
		||||
              style: TextStyle(
 | 
			
		||||
                color: textColor,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,8 @@ class MessageReactions extends StatelessWidget {
 | 
			
		||||
  final Event event;
 | 
			
		||||
  final Timeline timeline;
 | 
			
		||||
 | 
			
		||||
  const MessageReactions(this.event, this.timeline);
 | 
			
		||||
  const MessageReactions(this.event, this.timeline, {Key key})
 | 
			
		||||
      : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
 | 
			
		||||
@ -74,7 +74,7 @@ class ReplyContent extends StatelessWidget {
 | 
			
		||||
          height: fontSize * 2 + 6,
 | 
			
		||||
          color: lightText ? Colors.white : Theme.of(context).primaryColor,
 | 
			
		||||
        ),
 | 
			
		||||
        SizedBox(width: 6),
 | 
			
		||||
        const SizedBox(width: 6),
 | 
			
		||||
        Flexible(
 | 
			
		||||
          child: Column(
 | 
			
		||||
            crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,8 @@ import '../../config/app_config.dart';
 | 
			
		||||
class StateMessage extends StatelessWidget {
 | 
			
		||||
  final Event event;
 | 
			
		||||
  final void Function(String) unfold;
 | 
			
		||||
  const StateMessage(this.event, {@required this.unfold});
 | 
			
		||||
  const StateMessage(this.event, {@required this.unfold, Key key})
 | 
			
		||||
      : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
@ -52,7 +53,7 @@ class StateMessage extends StatelessWidget {
 | 
			
		||||
                if (counter != 0)
 | 
			
		||||
                  Text(
 | 
			
		||||
                    L10n.of(context).moreEvents(counter),
 | 
			
		||||
                    style: TextStyle(
 | 
			
		||||
                    style: const TextStyle(
 | 
			
		||||
                      fontWeight: FontWeight.bold,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,8 @@ class VerificationRequestContent extends StatelessWidget {
 | 
			
		||||
  final Event event;
 | 
			
		||||
  final Timeline timeline;
 | 
			
		||||
 | 
			
		||||
  const VerificationRequestContent({this.event, this.timeline});
 | 
			
		||||
  const VerificationRequestContent({this.event, this.timeline, Key key})
 | 
			
		||||
      : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
@ -22,13 +23,13 @@ class VerificationRequestContent extends StatelessWidget {
 | 
			
		||||
    final started = start.isNotEmpty;
 | 
			
		||||
    final canceled = cancel.isNotEmpty;
 | 
			
		||||
    return Padding(
 | 
			
		||||
      padding: EdgeInsets.symmetric(
 | 
			
		||||
      padding: const EdgeInsets.symmetric(
 | 
			
		||||
        horizontal: 8.0,
 | 
			
		||||
        vertical: 4.0,
 | 
			
		||||
      ),
 | 
			
		||||
      child: Center(
 | 
			
		||||
        child: Container(
 | 
			
		||||
          padding: EdgeInsets.all(8),
 | 
			
		||||
          padding: const EdgeInsets.all(8),
 | 
			
		||||
          decoration: BoxDecoration(
 | 
			
		||||
            border: Border.all(
 | 
			
		||||
              color: Theme.of(context).dividerColor,
 | 
			
		||||
@ -44,7 +45,7 @@ class VerificationRequestContent extends StatelessWidget {
 | 
			
		||||
                  color: canceled
 | 
			
		||||
                      ? Colors.red
 | 
			
		||||
                      : (fullyDone ? Colors.green : Colors.grey)),
 | 
			
		||||
              SizedBox(width: 8),
 | 
			
		||||
              const SizedBox(width: 8),
 | 
			
		||||
              Text(canceled
 | 
			
		||||
                  ? 'Error ${cancel.first.content.tryGet<String>('code')}: ${cancel.first.content.tryGet<String>('reason')}'
 | 
			
		||||
                  : (fullyDone
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,8 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
class FluffyBanner extends StatelessWidget {
 | 
			
		||||
  const FluffyBanner({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Image.asset(Theme.of(context).brightness == Brightness.dark
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,7 @@ class InputBar extends StatelessWidget {
 | 
			
		||||
  final ValueChanged<String> onChanged;
 | 
			
		||||
  final bool autofocus;
 | 
			
		||||
 | 
			
		||||
  InputBar({
 | 
			
		||||
  const InputBar({
 | 
			
		||||
    this.room,
 | 
			
		||||
    this.minLines,
 | 
			
		||||
    this.maxLines,
 | 
			
		||||
@ -36,7 +36,8 @@ class InputBar extends StatelessWidget {
 | 
			
		||||
    this.onChanged,
 | 
			
		||||
    this.autofocus,
 | 
			
		||||
    this.textInputAction,
 | 
			
		||||
  });
 | 
			
		||||
    Key key,
 | 
			
		||||
  }) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  List<Map<String, String>> getSuggestions(String text) {
 | 
			
		||||
    if (controller.selection.baseOffset != controller.selection.extentOffset ||
 | 
			
		||||
@ -220,7 +221,7 @@ class InputBar extends StatelessWidget {
 | 
			
		||||
        child: Column(
 | 
			
		||||
          crossAxisAlignment: CrossAxisAlignment.start,
 | 
			
		||||
          children: [
 | 
			
		||||
            Text('/' + command, style: TextStyle(fontFamily: 'monospace')),
 | 
			
		||||
            Text('/' + command, style: const TextStyle(fontFamily: 'monospace')),
 | 
			
		||||
            Text(_commandHint(L10n.of(context), command),
 | 
			
		||||
                style: Theme.of(context).textTheme.caption),
 | 
			
		||||
          ],
 | 
			
		||||
@ -246,7 +247,7 @@ class InputBar extends StatelessWidget {
 | 
			
		||||
              width: size,
 | 
			
		||||
              height: size,
 | 
			
		||||
            ),
 | 
			
		||||
            SizedBox(width: 6),
 | 
			
		||||
            const SizedBox(width: 6),
 | 
			
		||||
            Text(suggestion['name']),
 | 
			
		||||
            Expanded(
 | 
			
		||||
              child: Align(
 | 
			
		||||
@ -281,7 +282,7 @@ class InputBar extends StatelessWidget {
 | 
			
		||||
              size: size,
 | 
			
		||||
              client: client,
 | 
			
		||||
            ),
 | 
			
		||||
            SizedBox(width: 6),
 | 
			
		||||
            const SizedBox(width: 6),
 | 
			
		||||
            Text(suggestion['displayname'] ?? suggestion['mxid']),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
@ -395,7 +396,7 @@ class InputBar extends StatelessWidget {
 | 
			
		||||
          hideOnEmpty: true,
 | 
			
		||||
          hideOnLoading: true,
 | 
			
		||||
          keepSuggestionsOnSuggestionSelected: true,
 | 
			
		||||
          debounceDuration: Duration(
 | 
			
		||||
          debounceDuration: const Duration(
 | 
			
		||||
              milliseconds:
 | 
			
		||||
                  50), // show suggestions after 50ms idle time (default is 300)
 | 
			
		||||
          textFieldConfiguration: TextFieldConfiguration(
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,8 @@ import 'package:fluffychat/widgets/matrix.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
class LoadingView extends StatelessWidget {
 | 
			
		||||
  const LoadingView({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    if (Matrix.of(context)
 | 
			
		||||
@ -23,6 +25,6 @@ class LoadingView extends StatelessWidget {
 | 
			
		||||
        ),
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    return EmptyPage(loading: true);
 | 
			
		||||
    return const EmptyPage(loading: true);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ class MaxWidthBody extends StatelessWidget {
 | 
			
		||||
        );
 | 
			
		||||
        return withScrolling
 | 
			
		||||
            ? SingleChildScrollView(
 | 
			
		||||
                physics: ScrollPhysics(),
 | 
			
		||||
                physics: const ScrollPhysics(),
 | 
			
		||||
                child: Padding(
 | 
			
		||||
                  padding: padding,
 | 
			
		||||
                  child: child,
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@ class OnePageCard extends StatelessWidget {
 | 
			
		||||
            Matrix.of(context).client.isLogged()
 | 
			
		||||
        ? child
 | 
			
		||||
        : Container(
 | 
			
		||||
            decoration: BoxDecoration(
 | 
			
		||||
            decoration: const BoxDecoration(
 | 
			
		||||
              image: DecorationImage(
 | 
			
		||||
                image: AssetImage('assets/login_wallpaper.jpg'),
 | 
			
		||||
                fit: BoxFit.cover,
 | 
			
		||||
 | 
			
		||||
@ -28,9 +28,9 @@ class SideViewLayout extends StatelessWidget {
 | 
			
		||||
                    color: Theme.of(context).dividerColor,
 | 
			
		||||
                  ),
 | 
			
		||||
                  AnimatedContainer(
 | 
			
		||||
                    duration: Duration(milliseconds: 300),
 | 
			
		||||
                    duration: const Duration(milliseconds: 300),
 | 
			
		||||
                    clipBehavior: Clip.antiAlias,
 | 
			
		||||
                    decoration: BoxDecoration(),
 | 
			
		||||
                    decoration: const BoxDecoration(),
 | 
			
		||||
                    width: hideSideView ? 0 : 360.0,
 | 
			
		||||
                    child: hideSideView ? null : sideView,
 | 
			
		||||
                  ),
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@ class TwoColumnLayout extends StatelessWidget {
 | 
			
		||||
          children: [
 | 
			
		||||
            Container(
 | 
			
		||||
              clipBehavior: Clip.antiAlias,
 | 
			
		||||
              decoration: BoxDecoration(),
 | 
			
		||||
              decoration: const BoxDecoration(),
 | 
			
		||||
              width: 360.0,
 | 
			
		||||
              child: mainView,
 | 
			
		||||
            ),
 | 
			
		||||
 | 
			
		||||
@ -27,14 +27,17 @@ class ChatListItem extends StatelessWidget {
 | 
			
		||||
  final Function onTap;
 | 
			
		||||
  final Function onLongPress;
 | 
			
		||||
 | 
			
		||||
  const ChatListItem(this.room,
 | 
			
		||||
      {this.activeChat = false,
 | 
			
		||||
      this.selected = false,
 | 
			
		||||
      this.onTap,
 | 
			
		||||
      this.onLongPress,
 | 
			
		||||
      this.onForget});
 | 
			
		||||
  const ChatListItem(
 | 
			
		||||
    this.room, {
 | 
			
		||||
    this.activeChat = false,
 | 
			
		||||
    this.selected = false,
 | 
			
		||||
    this.onTap,
 | 
			
		||||
    this.onLongPress,
 | 
			
		||||
    this.onForget,
 | 
			
		||||
    Key key,
 | 
			
		||||
  }) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  void clickAction(BuildContext context) async {
 | 
			
		||||
  dynamic clickAction(BuildContext context) async {
 | 
			
		||||
    if (onTap != null) return onTap();
 | 
			
		||||
    if (!activeChat) {
 | 
			
		||||
      if (room.membership == Membership.invite &&
 | 
			
		||||
@ -161,13 +164,13 @@ class ChatListItem extends StatelessWidget {
 | 
			
		||||
          : Theme.of(context).secondaryHeaderColor,
 | 
			
		||||
      onLongPress: onLongPress,
 | 
			
		||||
      leading: selected
 | 
			
		||||
          ? Container(
 | 
			
		||||
          ? SizedBox(
 | 
			
		||||
              width: Avatar.defaultSize,
 | 
			
		||||
              height: Avatar.defaultSize,
 | 
			
		||||
              child: Material(
 | 
			
		||||
                color: Theme.of(context).primaryColor,
 | 
			
		||||
                borderRadius: BorderRadius.circular(Avatar.defaultSize),
 | 
			
		||||
                child: Icon(Icons.check, color: Colors.white),
 | 
			
		||||
                child: const Icon(Icons.check, color: Colors.white),
 | 
			
		||||
              ),
 | 
			
		||||
            )
 | 
			
		||||
          : Avatar(room.avatar, room.displayname, onTap: onLongPress),
 | 
			
		||||
@ -188,8 +191,8 @@ class ChatListItem extends StatelessWidget {
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          if (isMuted)
 | 
			
		||||
            Padding(
 | 
			
		||||
              padding: const EdgeInsets.only(left: 4.0),
 | 
			
		||||
            const Padding(
 | 
			
		||||
              padding: EdgeInsets.only(left: 4.0),
 | 
			
		||||
              child: Icon(
 | 
			
		||||
                Icons.notifications_off_outlined,
 | 
			
		||||
                size: 16,
 | 
			
		||||
@ -227,15 +230,15 @@ class ChatListItem extends StatelessWidget {
 | 
			
		||||
              room.lastEvent.statusIcon,
 | 
			
		||||
              size: 14,
 | 
			
		||||
            ),
 | 
			
		||||
            SizedBox(width: 4),
 | 
			
		||||
            const SizedBox(width: 4),
 | 
			
		||||
          },
 | 
			
		||||
          AnimatedContainer(
 | 
			
		||||
            width: typingText.isEmpty ? 0 : 18,
 | 
			
		||||
            clipBehavior: Clip.hardEdge,
 | 
			
		||||
            decoration: BoxDecoration(),
 | 
			
		||||
            duration: Duration(milliseconds: 300),
 | 
			
		||||
            decoration: const BoxDecoration(),
 | 
			
		||||
            duration: const Duration(milliseconds: 300),
 | 
			
		||||
            curve: Curves.bounceInOut,
 | 
			
		||||
            padding: EdgeInsets.only(right: 4),
 | 
			
		||||
            padding: const EdgeInsets.only(right: 4),
 | 
			
		||||
            child: Icon(
 | 
			
		||||
              Icons.edit_outlined,
 | 
			
		||||
              color: Theme.of(context).colorScheme.secondary,
 | 
			
		||||
@ -286,11 +289,11 @@ class ChatListItem extends StatelessWidget {
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
          ),
 | 
			
		||||
          SizedBox(width: 8),
 | 
			
		||||
          const SizedBox(width: 8),
 | 
			
		||||
          AnimatedContainer(
 | 
			
		||||
            duration: Duration(milliseconds: 300),
 | 
			
		||||
            duration: const Duration(milliseconds: 300),
 | 
			
		||||
            curve: Curves.bounceInOut,
 | 
			
		||||
            padding: EdgeInsets.symmetric(horizontal: 7),
 | 
			
		||||
            padding: const EdgeInsets.symmetric(horizontal: 7),
 | 
			
		||||
            height: unreadBubbleSize,
 | 
			
		||||
            width: room.notificationCount == 0 && !unread
 | 
			
		||||
                ? 0
 | 
			
		||||
@ -307,7 +310,7 @@ class ChatListItem extends StatelessWidget {
 | 
			
		||||
              child: room.notificationCount > 0
 | 
			
		||||
                  ? Text(
 | 
			
		||||
                      room.notificationCount.toString(),
 | 
			
		||||
                      style: TextStyle(
 | 
			
		||||
                      style: const TextStyle(
 | 
			
		||||
                        color: Colors.white,
 | 
			
		||||
                        fontSize: 13,
 | 
			
		||||
                      ),
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ import '../../pages/user_bottom_sheet.dart';
 | 
			
		||||
class ParticipantListItem extends StatelessWidget {
 | 
			
		||||
  final User user;
 | 
			
		||||
 | 
			
		||||
  const ParticipantListItem(this.user);
 | 
			
		||||
  const ParticipantListItem(this.user, {Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
@ -38,8 +38,8 @@ class ParticipantListItem extends StatelessWidget {
 | 
			
		||||
          permissionBatch.isEmpty
 | 
			
		||||
              ? Container()
 | 
			
		||||
              : Container(
 | 
			
		||||
                  padding: EdgeInsets.all(4),
 | 
			
		||||
                  margin: EdgeInsets.symmetric(horizontal: 8),
 | 
			
		||||
                  padding: const EdgeInsets.all(4),
 | 
			
		||||
                  margin: const EdgeInsets.symmetric(horizontal: 8),
 | 
			
		||||
                  decoration: BoxDecoration(
 | 
			
		||||
                    color: Theme.of(context).secondaryHeaderColor,
 | 
			
		||||
                    borderRadius: BorderRadius.circular(8),
 | 
			
		||||
@ -49,8 +49,8 @@ class ParticipantListItem extends StatelessWidget {
 | 
			
		||||
          membershipBatch[user.membership].isEmpty
 | 
			
		||||
              ? Container()
 | 
			
		||||
              : Container(
 | 
			
		||||
                  padding: EdgeInsets.all(4),
 | 
			
		||||
                  margin: EdgeInsets.symmetric(horizontal: 8),
 | 
			
		||||
                  padding: const EdgeInsets.all(4),
 | 
			
		||||
                  margin: const EdgeInsets.symmetric(horizontal: 8),
 | 
			
		||||
                  decoration: BoxDecoration(
 | 
			
		||||
                    color: Theme.of(context).secondaryHeaderColor,
 | 
			
		||||
                    borderRadius: BorderRadius.circular(8),
 | 
			
		||||
 | 
			
		||||
@ -69,13 +69,13 @@ class PermissionsListTile extends StatelessWidget {
 | 
			
		||||
      leading: CircleAvatar(
 | 
			
		||||
        backgroundColor: Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
        foregroundColor: Colors.grey,
 | 
			
		||||
        child: Icon(Icons.edit_attributes_outlined),
 | 
			
		||||
        child: const Icon(Icons.edit_attributes_outlined),
 | 
			
		||||
      ),
 | 
			
		||||
      title: Text(getLocalizedPowerLevelString(context)),
 | 
			
		||||
      subtitle: Row(
 | 
			
		||||
        children: [
 | 
			
		||||
          Container(
 | 
			
		||||
            padding: EdgeInsets.all(4),
 | 
			
		||||
            padding: const EdgeInsets.all(4),
 | 
			
		||||
            decoration: BoxDecoration(
 | 
			
		||||
              color: Theme.of(context).secondaryHeaderColor,
 | 
			
		||||
              borderRadius: BorderRadius.circular(8),
 | 
			
		||||
@ -84,7 +84,7 @@ class PermissionsListTile extends StatelessWidget {
 | 
			
		||||
              child: Text(permission.toString()),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          SizedBox(width: 8),
 | 
			
		||||
          const SizedBox(width: 8),
 | 
			
		||||
          Text(permission.toLocalizedPowerLevelString(context)),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
 | 
			
		||||
@ -110,7 +110,7 @@ class UserDeviceListItem extends StatelessWidget {
 | 
			
		||||
            maxLines: 1,
 | 
			
		||||
            overflow: TextOverflow.ellipsis,
 | 
			
		||||
          ),
 | 
			
		||||
          Spacer(),
 | 
			
		||||
          const Spacer(),
 | 
			
		||||
          if (userDevice.lastSeenTs != null)
 | 
			
		||||
            Text(DateTime.fromMillisecondsSinceEpoch(userDevice.lastSeenTs)
 | 
			
		||||
                .localizedTimeShort(context)),
 | 
			
		||||
@ -120,9 +120,9 @@ class UserDeviceListItem extends StatelessWidget {
 | 
			
		||||
        children: <Widget>[
 | 
			
		||||
          Text(
 | 
			
		||||
            userDevice.deviceId,
 | 
			
		||||
            style: TextStyle(fontWeight: FontWeight.w300),
 | 
			
		||||
            style: const TextStyle(fontWeight: FontWeight.w300),
 | 
			
		||||
          ),
 | 
			
		||||
          Spacer(),
 | 
			
		||||
          const Spacer(),
 | 
			
		||||
          if (keys != null)
 | 
			
		||||
            Text(
 | 
			
		||||
              keys.blocked
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,8 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
 | 
			
		||||
import 'layouts/one_page_card.dart';
 | 
			
		||||
 | 
			
		||||
class LockScreen extends StatefulWidget {
 | 
			
		||||
  const LockScreen({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  _LockScreenState createState() => _LockScreenState();
 | 
			
		||||
}
 | 
			
		||||
@ -42,7 +44,7 @@ class _LockScreenState extends State<LockScreen> {
 | 
			
		||||
                gradient: LinearGradient(
 | 
			
		||||
                  begin: Alignment.topRight,
 | 
			
		||||
                  end: Alignment.bottomLeft,
 | 
			
		||||
                  stops: [
 | 
			
		||||
                  stops: const [
 | 
			
		||||
                    0.1,
 | 
			
		||||
                    0.4,
 | 
			
		||||
                    0.6,
 | 
			
		||||
@ -62,12 +64,12 @@ class _LockScreenState extends State<LockScreen> {
 | 
			
		||||
                controller: _textEditingController,
 | 
			
		||||
                focusNode: _focusNode,
 | 
			
		||||
                pinBoxRadius: AppConfig.borderRadius,
 | 
			
		||||
                pinTextStyle: TextStyle(fontSize: 32),
 | 
			
		||||
                pinTextStyle: const TextStyle(fontSize: 32),
 | 
			
		||||
                hideCharacter: true,
 | 
			
		||||
                hasError: _wrongInput,
 | 
			
		||||
                onDone: (String input) async {
 | 
			
		||||
                  if (input ==
 | 
			
		||||
                      await FlutterSecureStorage()
 | 
			
		||||
                      await const FlutterSecureStorage()
 | 
			
		||||
                          .read(key: SettingKeys.appLockKey)) {
 | 
			
		||||
                    AppLock.of(context).didUnlock();
 | 
			
		||||
                  } else {
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,8 @@ import 'package:matrix/matrix.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
class LogViewer extends StatefulWidget {
 | 
			
		||||
  const LogViewer({Key key}) : super(key: key);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  _LogViewerState createState() => _LogViewerState();
 | 
			
		||||
}
 | 
			
		||||
@ -19,14 +21,14 @@ class _LogViewerState extends State<LogViewer> {
 | 
			
		||||
      backgroundColor: Colors.black,
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        title: Text(logLevel.toString()),
 | 
			
		||||
        leading: BackButton(),
 | 
			
		||||
        leading: const BackButton(),
 | 
			
		||||
        actions: [
 | 
			
		||||
          IconButton(
 | 
			
		||||
            icon: Icon(Icons.zoom_in_outlined),
 | 
			
		||||
            icon: const Icon(Icons.zoom_in_outlined),
 | 
			
		||||
            onPressed: () => setState(() => fontSize++),
 | 
			
		||||
          ),
 | 
			
		||||
          IconButton(
 | 
			
		||||
            icon: Icon(Icons.zoom_out_outlined),
 | 
			
		||||
            icon: const Icon(Icons.zoom_out_outlined),
 | 
			
		||||
            onPressed: () => setState(() => fontSize--),
 | 
			
		||||
          ),
 | 
			
		||||
          PopupMenuButton<Level>(
 | 
			
		||||
 | 
			
		||||
@ -41,7 +41,7 @@ class Matrix extends StatefulWidget {
 | 
			
		||||
 | 
			
		||||
  final Map<String, String> queryParameters;
 | 
			
		||||
 | 
			
		||||
  Matrix({
 | 
			
		||||
  const Matrix({
 | 
			
		||||
    this.child,
 | 
			
		||||
    @required this.router,
 | 
			
		||||
    @required this.context,
 | 
			
		||||
@ -209,7 +209,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
 | 
			
		||||
 | 
			
		||||
  set cachedPassword(String p) => _cachedPassword = p;
 | 
			
		||||
 | 
			
		||||
  void _onUiaRequest(UiaRequest uiaRequest) async {
 | 
			
		||||
  Future _onUiaRequest(UiaRequest uiaRequest) async {
 | 
			
		||||
    try {
 | 
			
		||||
      if (uiaRequest.state != UiaRequestState.waitForUser ||
 | 
			
		||||
          uiaRequest.nextStages.isEmpty) return;
 | 
			
		||||
@ -224,7 +224,7 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
 | 
			
		||||
                okLabel: L10n.of(widget.context).ok,
 | 
			
		||||
                cancelLabel: L10n.of(widget.context).cancel,
 | 
			
		||||
                textFields: [
 | 
			
		||||
                  DialogTextField(
 | 
			
		||||
                  const DialogTextField(
 | 
			
		||||
                    minLines: 1,
 | 
			
		||||
                    maxLines: 1,
 | 
			
		||||
                    obscureText: true,
 | 
			
		||||
@ -481,7 +481,9 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
 | 
			
		||||
    // Display the app lock
 | 
			
		||||
    if (PlatformInfos.isMobile) {
 | 
			
		||||
      WidgetsBinding.instance.addPostFrameCallback((_) {
 | 
			
		||||
        FlutterSecureStorage().read(key: SettingKeys.appLockKey).then((lock) {
 | 
			
		||||
        const FlutterSecureStorage()
 | 
			
		||||
            .read(key: SettingKeys.appLockKey)
 | 
			
		||||
            .then((lock) {
 | 
			
		||||
          if (lock?.isNotEmpty ?? false) {
 | 
			
		||||
            AppLock.of(widget.context).enable();
 | 
			
		||||
            AppLock.of(widget.context).showLockScreen();
 | 
			
		||||
@ -507,11 +509,11 @@ class MatrixState extends State<Matrix> with WidgetsBindingObserver {
 | 
			
		||||
        context,
 | 
			
		||||
        widget.router,
 | 
			
		||||
        onFcmError: (errorMsg, {Uri link}) => Timer(
 | 
			
		||||
          Duration(seconds: 1),
 | 
			
		||||
          const Duration(seconds: 1),
 | 
			
		||||
          () {
 | 
			
		||||
            final banner = SnackBar(
 | 
			
		||||
              content: Text(errorMsg),
 | 
			
		||||
              duration: Duration(seconds: 30),
 | 
			
		||||
              duration: const Duration(seconds: 30),
 | 
			
		||||
              action: link == null
 | 
			
		||||
                  ? null
 | 
			
		||||
                  : SnackBarAction(
 | 
			
		||||
 | 
			
		||||
@ -45,7 +45,7 @@ class ProfileBottomSheet extends StatelessWidget {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Center(
 | 
			
		||||
      child: Container(
 | 
			
		||||
      child: SizedBox(
 | 
			
		||||
        width: min(
 | 
			
		||||
            MediaQuery.of(context).size.width, FluffyThemes.columnWidth * 1.5),
 | 
			
		||||
        child: Material(
 | 
			
		||||
@ -58,7 +58,7 @@ class ProfileBottomSheet extends StatelessWidget {
 | 
			
		||||
                backgroundColor:
 | 
			
		||||
                    Theme.of(context).scaffoldBackgroundColor.withOpacity(0.5),
 | 
			
		||||
                leading: IconButton(
 | 
			
		||||
                  icon: Icon(Icons.arrow_downward_outlined),
 | 
			
		||||
                  icon: const Icon(Icons.arrow_downward_outlined),
 | 
			
		||||
                  onPressed: Navigator.of(context, rootNavigator: false).pop,
 | 
			
		||||
                  tooltip: L10n.of(context).close,
 | 
			
		||||
                ),
 | 
			
		||||
@ -79,7 +79,7 @@ class ProfileBottomSheet extends StatelessWidget {
 | 
			
		||||
                                  child: snapshot.hasError
 | 
			
		||||
                                      ? Text(snapshot.error
 | 
			
		||||
                                          .toLocalizedString(context))
 | 
			
		||||
                                      : CircularProgressIndicator.adaptive(
 | 
			
		||||
                                      : const CircularProgressIndicator.adaptive(
 | 
			
		||||
                                          strokeWidth: 2),
 | 
			
		||||
                                )
 | 
			
		||||
                              : ContentBanner(
 | 
			
		||||
@ -91,18 +91,18 @@ class ProfileBottomSheet extends StatelessWidget {
 | 
			
		||||
                        ListTile(
 | 
			
		||||
                          title: Text(profile?.displayName ?? userId.localpart),
 | 
			
		||||
                          subtitle: Text(userId),
 | 
			
		||||
                          trailing: Icon(Icons.account_box_outlined),
 | 
			
		||||
                          trailing: const Icon(Icons.account_box_outlined),
 | 
			
		||||
                        ),
 | 
			
		||||
                        Container(
 | 
			
		||||
                          width: double.infinity,
 | 
			
		||||
                          padding: EdgeInsets.all(12),
 | 
			
		||||
                          padding: const EdgeInsets.all(12),
 | 
			
		||||
                          child: ElevatedButton.icon(
 | 
			
		||||
                            onPressed: () => _startDirectChat(context),
 | 
			
		||||
                            label: Text(L10n.of(context).newChat),
 | 
			
		||||
                            icon: Icon(Icons.send_outlined),
 | 
			
		||||
                            icon: const Icon(Icons.send_outlined),
 | 
			
		||||
                          ),
 | 
			
		||||
                        ),
 | 
			
		||||
                        SizedBox(height: 8),
 | 
			
		||||
                        const SizedBox(height: 8),
 | 
			
		||||
                      ],
 | 
			
		||||
                    );
 | 
			
		||||
                  }),
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ class UnreadBadgeBackButton extends StatelessWidget {
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Stack(
 | 
			
		||||
      children: [
 | 
			
		||||
        Center(child: BackButton()),
 | 
			
		||||
        const Center(child: BackButton()),
 | 
			
		||||
        StreamBuilder(
 | 
			
		||||
            stream: Matrix.of(context).client.onSync.stream,
 | 
			
		||||
            builder: (context, _) {
 | 
			
		||||
@ -31,8 +31,8 @@ class UnreadBadgeBackButton extends StatelessWidget {
 | 
			
		||||
                  ? Align(
 | 
			
		||||
                      alignment: Alignment.bottomRight,
 | 
			
		||||
                      child: Container(
 | 
			
		||||
                        padding: EdgeInsets.all(4),
 | 
			
		||||
                        margin: EdgeInsets.only(bottom: 4, right: 8),
 | 
			
		||||
                        padding: const EdgeInsets.all(4),
 | 
			
		||||
                        margin: const EdgeInsets.only(bottom: 4, right: 8),
 | 
			
		||||
                        decoration: BoxDecoration(
 | 
			
		||||
                          color: Theme.of(context).primaryColor,
 | 
			
		||||
                          borderRadius:
 | 
			
		||||
@ -40,7 +40,7 @@ class UnreadBadgeBackButton extends StatelessWidget {
 | 
			
		||||
                        ),
 | 
			
		||||
                        child: Text(
 | 
			
		||||
                          '$unreadCount',
 | 
			
		||||
                          style: TextStyle(
 | 
			
		||||
                          style: const TextStyle(
 | 
			
		||||
                            fontSize: 12,
 | 
			
		||||
                            color: Colors.white,
 | 
			
		||||
                          ),
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										16
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								pubspec.lock
									
									
									
									
									
								
							@ -384,6 +384,13 @@ packages:
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.0.0"
 | 
			
		||||
  flutter_lints:
 | 
			
		||||
    dependency: "direct dev"
 | 
			
		||||
    description:
 | 
			
		||||
      name: flutter_lints
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.0.4"
 | 
			
		||||
  flutter_local_notifications:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
@ -665,6 +672,13 @@ packages:
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "0.8.0"
 | 
			
		||||
  lints:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: lints
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "1.0.1"
 | 
			
		||||
  lists:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
@ -913,7 +927,7 @@ packages:
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "2.0.0"
 | 
			
		||||
  pedantic:
 | 
			
		||||
    dependency: "direct dev"
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: pedantic
 | 
			
		||||
      url: "https://pub.dartlang.org"
 | 
			
		||||
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user