mirror of
				https://gitlab.com/famedly/fluffychat.git
				synced 2025-11-04 06:17:26 +01:00 
			
		
		
		
	Merge branch 'krille/redesign-login' into 'main'
design: Improve login design See merge request famedly/fluffychat!1013
This commit is contained in:
		
						commit
						ef0d54d11f
					
				@ -19,13 +19,11 @@ class ConnectPageView extends StatelessWidget {
 | 
			
		||||
    final identityProviders = controller.identityProviders;
 | 
			
		||||
    return LoginScaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading:
 | 
			
		||||
            controller.loading ? null : const BackButton(color: Colors.white),
 | 
			
		||||
        leading: controller.loading ? null : const BackButton(),
 | 
			
		||||
        automaticallyImplyLeading: !controller.loading,
 | 
			
		||||
        centerTitle: true,
 | 
			
		||||
        title: Text(
 | 
			
		||||
          Matrix.of(context).getLoginClient().homeserver?.host ?? '',
 | 
			
		||||
          style: const TextStyle(color: Colors.white),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
      body: ListView(
 | 
			
		||||
@ -38,15 +36,19 @@ class ConnectPageView extends StatelessWidget {
 | 
			
		||||
                  children: [
 | 
			
		||||
                    Material(
 | 
			
		||||
                      borderRadius: BorderRadius.circular(64),
 | 
			
		||||
                      elevation: 10,
 | 
			
		||||
                      elevation: Theme.of(context)
 | 
			
		||||
                              .appBarTheme
 | 
			
		||||
                              .scrolledUnderElevation ??
 | 
			
		||||
                          4,
 | 
			
		||||
                      color: Colors.transparent,
 | 
			
		||||
                      shadowColor: Theme.of(context).appBarTheme.shadowColor,
 | 
			
		||||
                      clipBehavior: Clip.hardEdge,
 | 
			
		||||
                      child: CircleAvatar(
 | 
			
		||||
                        radius: 64,
 | 
			
		||||
                        backgroundColor: Colors.white.withAlpha(200),
 | 
			
		||||
                        backgroundColor: Colors.white,
 | 
			
		||||
                        child: avatar == null
 | 
			
		||||
                            ? const Icon(
 | 
			
		||||
                                Icons.person_outlined,
 | 
			
		||||
                                Icons.person,
 | 
			
		||||
                                color: Colors.black,
 | 
			
		||||
                                size: 64,
 | 
			
		||||
                              )
 | 
			
		||||
@ -93,10 +95,7 @@ class ConnectPageView extends StatelessWidget {
 | 
			
		||||
                  hintText: L10n.of(context)!.chooseAUsername,
 | 
			
		||||
                  errorText: controller.signupError,
 | 
			
		||||
                  errorStyle: const TextStyle(color: Colors.orange),
 | 
			
		||||
                  fillColor: Theme.of(context)
 | 
			
		||||
                      .colorScheme
 | 
			
		||||
                      .background
 | 
			
		||||
                      .withOpacity(0.75),
 | 
			
		||||
                  fillColor: Theme.of(context).colorScheme.background,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
@ -105,6 +104,10 @@ class ConnectPageView extends StatelessWidget {
 | 
			
		||||
              child: Hero(
 | 
			
		||||
                tag: 'loginButton',
 | 
			
		||||
                child: ElevatedButton(
 | 
			
		||||
                  style: ElevatedButton.styleFrom(
 | 
			
		||||
                    backgroundColor: Theme.of(context).colorScheme.primary,
 | 
			
		||||
                    foregroundColor: Theme.of(context).colorScheme.onPrimary,
 | 
			
		||||
                  ),
 | 
			
		||||
                  onPressed: controller.loading ? () {} : controller.signUp,
 | 
			
		||||
                  child: controller.loading
 | 
			
		||||
                      ? const LinearProgressIndicator()
 | 
			
		||||
@ -112,45 +115,52 @@ class ConnectPageView extends StatelessWidget {
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            Row(
 | 
			
		||||
              children: [
 | 
			
		||||
                const Expanded(
 | 
			
		||||
            Padding(
 | 
			
		||||
              padding: const EdgeInsets.symmetric(horizontal: 16.0),
 | 
			
		||||
              child: Row(
 | 
			
		||||
                children: [
 | 
			
		||||
                  Expanded(
 | 
			
		||||
                    child: Divider(
 | 
			
		||||
                  color: Colors.white,
 | 
			
		||||
                  thickness: 1,
 | 
			
		||||
                )),
 | 
			
		||||
                Padding(
 | 
			
		||||
                  padding: const EdgeInsets.all(12.0),
 | 
			
		||||
                  child: Text(
 | 
			
		||||
                    L10n.of(context)!.or,
 | 
			
		||||
                    style: const TextStyle(
 | 
			
		||||
                      color: Colors.white,
 | 
			
		||||
                      fontSize: 18,
 | 
			
		||||
                      thickness: 1,
 | 
			
		||||
                      color: Theme.of(context).textTheme.subtitle1?.color,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                const Expanded(
 | 
			
		||||
                  Padding(
 | 
			
		||||
                    padding: const EdgeInsets.all(12.0),
 | 
			
		||||
                    child: Text(
 | 
			
		||||
                      L10n.of(context)!.or,
 | 
			
		||||
                      style: const TextStyle(fontSize: 18),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                  Expanded(
 | 
			
		||||
                    child: Divider(
 | 
			
		||||
                  color: Colors.white,
 | 
			
		||||
                  thickness: 1,
 | 
			
		||||
                )),
 | 
			
		||||
              ],
 | 
			
		||||
                      thickness: 1,
 | 
			
		||||
                      color: Theme.of(context).textTheme.subtitle1?.color,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
          if (controller.supportsSso)
 | 
			
		||||
            identityProviders == null
 | 
			
		||||
                ? const SizedBox(
 | 
			
		||||
                    height: 74,
 | 
			
		||||
                    child: Center(
 | 
			
		||||
                        child: CircularProgressIndicator.adaptive(
 | 
			
		||||
                      backgroundColor: Colors.white,
 | 
			
		||||
                    )),
 | 
			
		||||
                    child: Center(child: CircularProgressIndicator.adaptive()),
 | 
			
		||||
                  )
 | 
			
		||||
                : Center(
 | 
			
		||||
                    child: identityProviders.length == 1
 | 
			
		||||
                        ? Padding(
 | 
			
		||||
                            padding: const EdgeInsets.all(12.0),
 | 
			
		||||
                            child: ElevatedButton(
 | 
			
		||||
                              style: ElevatedButton.styleFrom(
 | 
			
		||||
                                backgroundColor: Theme.of(context)
 | 
			
		||||
                                    .colorScheme
 | 
			
		||||
                                    .primaryContainer,
 | 
			
		||||
                                foregroundColor: Theme.of(context)
 | 
			
		||||
                                    .colorScheme
 | 
			
		||||
                                    .onPrimaryContainer,
 | 
			
		||||
                              ),
 | 
			
		||||
                              onPressed: () => controller
 | 
			
		||||
                                  .ssoLoginAction(identityProviders.single.id!),
 | 
			
		||||
                              child: Text(identityProviders.single.name ??
 | 
			
		||||
@ -175,6 +185,12 @@ class ConnectPageView extends StatelessWidget {
 | 
			
		||||
              child: Hero(
 | 
			
		||||
                tag: 'signinButton',
 | 
			
		||||
                child: ElevatedButton(
 | 
			
		||||
                  style: ElevatedButton.styleFrom(
 | 
			
		||||
                    backgroundColor:
 | 
			
		||||
                        Theme.of(context).colorScheme.primaryContainer,
 | 
			
		||||
                    foregroundColor:
 | 
			
		||||
                        Theme.of(context).colorScheme.onPrimaryContainer,
 | 
			
		||||
                  ),
 | 
			
		||||
                  onPressed: controller.loading ? () {} : controller.login,
 | 
			
		||||
                  child: Text(L10n.of(context)!.login),
 | 
			
		||||
                ),
 | 
			
		||||
 | 
			
		||||
@ -52,7 +52,6 @@ class SsoButton extends StatelessWidget {
 | 
			
		||||
              style: const TextStyle(
 | 
			
		||||
                fontSize: 12,
 | 
			
		||||
                fontWeight: FontWeight.bold,
 | 
			
		||||
                color: Colors.white,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
 | 
			
		||||
@ -180,12 +180,13 @@ class HomeserverPickerController extends State<HomeserverPicker> {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> restoreBackup() async {
 | 
			
		||||
    final file =
 | 
			
		||||
        await FilePickerCross.importFromStorage(fileExtension: '.fluffybackup');
 | 
			
		||||
    if (file.fileName == null) return;
 | 
			
		||||
    await showFutureLoadingDialog(
 | 
			
		||||
        context: context,
 | 
			
		||||
        future: () async {
 | 
			
		||||
          try {
 | 
			
		||||
            final file = await FilePickerCross.importFromStorage(
 | 
			
		||||
                fileExtension: '.fluffybackup');
 | 
			
		||||
            final client = Matrix.of(context).getLoginClient();
 | 
			
		||||
            await client.importDump(file.toString());
 | 
			
		||||
            Matrix.of(context).initMatrix();
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,6 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
 | 
			
		||||
import 'package:url_launcher/url_launcher.dart';
 | 
			
		||||
 | 
			
		||||
import 'package:fluffychat/config/app_config.dart';
 | 
			
		||||
import 'package:fluffychat/utils/platform_infos.dart';
 | 
			
		||||
import 'package:fluffychat/widgets/layouts/login_scaffold.dart';
 | 
			
		||||
import 'homeserver_picker.dart';
 | 
			
		||||
 | 
			
		||||
@ -17,147 +16,157 @@ class HomeserverPickerView extends StatelessWidget {
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final benchmarkResults = controller.benchmarkResults;
 | 
			
		||||
    return LoginScaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        actions: [
 | 
			
		||||
          IconButton(
 | 
			
		||||
            onPressed: controller.restoreBackup,
 | 
			
		||||
            tooltip: L10n.of(context)!.hydrate,
 | 
			
		||||
            color: Colors.white,
 | 
			
		||||
            icon: const Icon(Icons.restore_outlined),
 | 
			
		||||
          ),
 | 
			
		||||
          IconButton(
 | 
			
		||||
            tooltip: L10n.of(context)!.privacy,
 | 
			
		||||
            onPressed: () => launch(AppConfig.privacyUrl),
 | 
			
		||||
            color: Colors.white,
 | 
			
		||||
            icon: const Icon(Icons.shield_outlined),
 | 
			
		||||
          ),
 | 
			
		||||
          IconButton(
 | 
			
		||||
            tooltip: L10n.of(context)!.about,
 | 
			
		||||
            onPressed: () => PlatformInfos.showDialog(context),
 | 
			
		||||
            color: Colors.white,
 | 
			
		||||
            icon: const Icon(Icons.info_outlined),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
      body: SafeArea(
 | 
			
		||||
        child: Column(
 | 
			
		||||
          children: [
 | 
			
		||||
            // display a prominent banner to import session for TOR browser
 | 
			
		||||
            // users. This feature is just some UX sugar as TOR users are
 | 
			
		||||
            // usually forced to logout as TOR browser is non-persistent
 | 
			
		||||
            AnimatedContainer(
 | 
			
		||||
              height: controller.isTorBrowser ? 64 : 0,
 | 
			
		||||
              duration: const Duration(milliseconds: 300),
 | 
			
		||||
      body: Column(
 | 
			
		||||
        children: [
 | 
			
		||||
          // display a prominent banner to import session for TOR browser
 | 
			
		||||
          // users. This feature is just some UX sugar as TOR users are
 | 
			
		||||
          // usually forced to logout as TOR browser is non-persistent
 | 
			
		||||
          AnimatedContainer(
 | 
			
		||||
            height: controller.isTorBrowser ? 64 : 0,
 | 
			
		||||
            duration: const Duration(milliseconds: 300),
 | 
			
		||||
            clipBehavior: Clip.hardEdge,
 | 
			
		||||
            curve: Curves.bounceInOut,
 | 
			
		||||
            decoration: const BoxDecoration(),
 | 
			
		||||
            child: Material(
 | 
			
		||||
              clipBehavior: Clip.hardEdge,
 | 
			
		||||
              curve: Curves.bounceInOut,
 | 
			
		||||
              decoration: const BoxDecoration(),
 | 
			
		||||
              child: Material(
 | 
			
		||||
                clipBehavior: Clip.hardEdge,
 | 
			
		||||
                borderRadius:
 | 
			
		||||
                    const BorderRadius.vertical(bottom: Radius.circular(8)),
 | 
			
		||||
                color: Theme.of(context).colorScheme.surface,
 | 
			
		||||
                child: ListTile(
 | 
			
		||||
                  leading: const Icon(Icons.vpn_key),
 | 
			
		||||
                  title: Text(L10n.of(context)!.hydrateTor),
 | 
			
		||||
                  subtitle: Text(L10n.of(context)!.hydrateTorLong),
 | 
			
		||||
                  trailing: const Icon(Icons.chevron_right_outlined),
 | 
			
		||||
                  onTap: controller.restoreBackup,
 | 
			
		||||
                ),
 | 
			
		||||
              borderRadius:
 | 
			
		||||
                  const BorderRadius.vertical(bottom: Radius.circular(8)),
 | 
			
		||||
              color: Theme.of(context).colorScheme.surface,
 | 
			
		||||
              child: ListTile(
 | 
			
		||||
                leading: const Icon(Icons.vpn_key),
 | 
			
		||||
                title: Text(L10n.of(context)!.hydrateTor),
 | 
			
		||||
                subtitle: Text(L10n.of(context)!.hydrateTorLong),
 | 
			
		||||
                trailing: const Icon(Icons.chevron_right_outlined),
 | 
			
		||||
                onTap: controller.restoreBackup,
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            Expanded(
 | 
			
		||||
              child: ListView(
 | 
			
		||||
                children: [
 | 
			
		||||
                  Container(
 | 
			
		||||
                    alignment: Alignment.center,
 | 
			
		||||
                    height: 200,
 | 
			
		||||
                    child: Image.asset('assets/info-logo.png'),
 | 
			
		||||
          ),
 | 
			
		||||
          Expanded(
 | 
			
		||||
            child: ListView(
 | 
			
		||||
              children: [
 | 
			
		||||
                Container(
 | 
			
		||||
                  alignment: Alignment.center,
 | 
			
		||||
                  height: 190,
 | 
			
		||||
                  child: Image.asset('assets/info-logo.png'),
 | 
			
		||||
                ),
 | 
			
		||||
                Padding(
 | 
			
		||||
                  padding: const EdgeInsets.all(12.0),
 | 
			
		||||
                  child: TextField(
 | 
			
		||||
                    focusNode: controller.homeserverFocusNode,
 | 
			
		||||
                    controller: controller.homeserverController,
 | 
			
		||||
                    onChanged: controller.onChanged,
 | 
			
		||||
                    decoration: InputDecoration(
 | 
			
		||||
                      prefixText: '${L10n.of(context)!.homeserver}: ',
 | 
			
		||||
                      hintText: L10n.of(context)!.enterYourHomeserver,
 | 
			
		||||
                      suffixIcon: const Icon(Icons.search),
 | 
			
		||||
                      errorText: controller.error,
 | 
			
		||||
                      fillColor: Theme.of(context).backgroundColor,
 | 
			
		||||
                    ),
 | 
			
		||||
                    readOnly: !AppConfig.allowOtherHomeservers,
 | 
			
		||||
                    onSubmitted: (_) => controller.checkHomeserverAction(),
 | 
			
		||||
                    autocorrect: false,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
                if (controller.displayServerList)
 | 
			
		||||
                  Padding(
 | 
			
		||||
                    padding: const EdgeInsets.all(12.0),
 | 
			
		||||
                    child: TextField(
 | 
			
		||||
                      focusNode: controller.homeserverFocusNode,
 | 
			
		||||
                      controller: controller.homeserverController,
 | 
			
		||||
                      onChanged: controller.onChanged,
 | 
			
		||||
                      decoration: InputDecoration(
 | 
			
		||||
                        prefixText: '${L10n.of(context)!.homeserver}: ',
 | 
			
		||||
                        hintText: L10n.of(context)!.enterYourHomeserver,
 | 
			
		||||
                        suffixIcon: const Icon(Icons.search),
 | 
			
		||||
                        errorText: controller.error,
 | 
			
		||||
                        fillColor: Theme.of(context)
 | 
			
		||||
                            .colorScheme
 | 
			
		||||
                            .background
 | 
			
		||||
                            .withOpacity(0.75),
 | 
			
		||||
                    child: Material(
 | 
			
		||||
                      borderRadius:
 | 
			
		||||
                          BorderRadius.circular(AppConfig.borderRadius),
 | 
			
		||||
                      color: Theme.of(context).colorScheme.onInverseSurface,
 | 
			
		||||
                      clipBehavior: Clip.hardEdge,
 | 
			
		||||
                      child: benchmarkResults == null
 | 
			
		||||
                          ? const Center(
 | 
			
		||||
                              child: Padding(
 | 
			
		||||
                              padding: EdgeInsets.all(12.0),
 | 
			
		||||
                              child: CircularProgressIndicator.adaptive(),
 | 
			
		||||
                            ))
 | 
			
		||||
                          : Column(
 | 
			
		||||
                              children: controller.filteredHomeservers
 | 
			
		||||
                                  .map(
 | 
			
		||||
                                    (server) => ListTile(
 | 
			
		||||
                                      trailing: IconButton(
 | 
			
		||||
                                        icon: const Icon(
 | 
			
		||||
                                          Icons.info_outlined,
 | 
			
		||||
                                          color: Colors.black,
 | 
			
		||||
                                        ),
 | 
			
		||||
                                        onPressed: () =>
 | 
			
		||||
                                            controller.showServerInfo(server),
 | 
			
		||||
                                      ),
 | 
			
		||||
                                      onTap: () => controller.setServer(
 | 
			
		||||
                                          server.homeserver.baseUrl.host),
 | 
			
		||||
                                      title: Text(
 | 
			
		||||
                                        server.homeserver.baseUrl.host,
 | 
			
		||||
                                        style: const TextStyle(
 | 
			
		||||
                                            color: Colors.black),
 | 
			
		||||
                                      ),
 | 
			
		||||
                                      subtitle: Text(
 | 
			
		||||
                                        server.homeserver.description ?? '',
 | 
			
		||||
                                        style: TextStyle(
 | 
			
		||||
                                            color: Colors.grey.shade700),
 | 
			
		||||
                                      ),
 | 
			
		||||
                                    ),
 | 
			
		||||
                                  )
 | 
			
		||||
                                  .toList(),
 | 
			
		||||
                            ),
 | 
			
		||||
                    ),
 | 
			
		||||
                  )
 | 
			
		||||
                else
 | 
			
		||||
                  Padding(
 | 
			
		||||
                    padding: const EdgeInsets.all(16.0),
 | 
			
		||||
                    child: Center(
 | 
			
		||||
                      child: Text(
 | 
			
		||||
                        AppConfig.applicationWelcomeMessage ??
 | 
			
		||||
                            L10n.of(context)!.welcomeText,
 | 
			
		||||
                        textAlign: TextAlign.center,
 | 
			
		||||
                        style: const TextStyle(fontSize: 20),
 | 
			
		||||
                      ),
 | 
			
		||||
                      readOnly: !AppConfig.allowOtherHomeservers,
 | 
			
		||||
                      onSubmitted: (_) => controller.checkHomeserverAction(),
 | 
			
		||||
                      autocorrect: false,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                  if (controller.displayServerList)
 | 
			
		||||
                    Padding(
 | 
			
		||||
                      padding: const EdgeInsets.all(12.0),
 | 
			
		||||
                      child: Material(
 | 
			
		||||
                        borderRadius:
 | 
			
		||||
                            BorderRadius.circular(AppConfig.borderRadius),
 | 
			
		||||
                        color: Colors.white.withAlpha(200),
 | 
			
		||||
                        clipBehavior: Clip.hardEdge,
 | 
			
		||||
                        child: benchmarkResults == null
 | 
			
		||||
                            ? const Center(
 | 
			
		||||
                                child: Padding(
 | 
			
		||||
                                padding: EdgeInsets.all(12.0),
 | 
			
		||||
                                child: CircularProgressIndicator.adaptive(),
 | 
			
		||||
                              ))
 | 
			
		||||
                            : Column(
 | 
			
		||||
                                children: controller.filteredHomeservers
 | 
			
		||||
                                    .map(
 | 
			
		||||
                                      (server) => ListTile(
 | 
			
		||||
                                        trailing: IconButton(
 | 
			
		||||
                                          icon: const Icon(
 | 
			
		||||
                                            Icons.info_outlined,
 | 
			
		||||
                                            color: Colors.black,
 | 
			
		||||
                                          ),
 | 
			
		||||
                                          onPressed: () =>
 | 
			
		||||
                                              controller.showServerInfo(server),
 | 
			
		||||
                                        ),
 | 
			
		||||
                                        onTap: () => controller.setServer(
 | 
			
		||||
                                            server.homeserver.baseUrl.host),
 | 
			
		||||
                                        title: Text(
 | 
			
		||||
                                          server.homeserver.baseUrl.host,
 | 
			
		||||
                                          style: const TextStyle(
 | 
			
		||||
                                              color: Colors.black),
 | 
			
		||||
                                        ),
 | 
			
		||||
                                        subtitle: Text(
 | 
			
		||||
                                          server.homeserver.description ?? '',
 | 
			
		||||
                                          style: TextStyle(
 | 
			
		||||
                                              color: Colors.grey.shade700),
 | 
			
		||||
                                        ),
 | 
			
		||||
                                      ),
 | 
			
		||||
                                    )
 | 
			
		||||
                                    .toList(),
 | 
			
		||||
                              ),
 | 
			
		||||
                      ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
          SafeArea(
 | 
			
		||||
            child: Container(
 | 
			
		||||
              padding: const EdgeInsets.all(12),
 | 
			
		||||
              width: double.infinity,
 | 
			
		||||
              child: Column(
 | 
			
		||||
                mainAxisSize: MainAxisSize.min,
 | 
			
		||||
                crossAxisAlignment: CrossAxisAlignment.stretch,
 | 
			
		||||
                children: [
 | 
			
		||||
                  TextButton(
 | 
			
		||||
                    style: TextButton.styleFrom(
 | 
			
		||||
                      foregroundColor:
 | 
			
		||||
                          Theme.of(context).colorScheme.onSurfaceVariant,
 | 
			
		||||
                    ),
 | 
			
		||||
                    onPressed: controller.restoreBackup,
 | 
			
		||||
                    child: Text(L10n.of(context)!.hydrate),
 | 
			
		||||
                  ),
 | 
			
		||||
                  TextButton(
 | 
			
		||||
                    onPressed: () => launch(AppConfig.privacyUrl),
 | 
			
		||||
                    child: Text(L10n.of(context)!.privacy),
 | 
			
		||||
                  ),
 | 
			
		||||
                  Hero(
 | 
			
		||||
                    tag: 'loginButton',
 | 
			
		||||
                    child: ElevatedButton(
 | 
			
		||||
                      style: ElevatedButton.styleFrom(
 | 
			
		||||
                        backgroundColor: Theme.of(context).colorScheme.primary,
 | 
			
		||||
                        foregroundColor:
 | 
			
		||||
                            Theme.of(context).colorScheme.onPrimary,
 | 
			
		||||
                      ),
 | 
			
		||||
                      onPressed: controller.isLoading
 | 
			
		||||
                          ? null
 | 
			
		||||
                          : controller.checkHomeserverAction,
 | 
			
		||||
                      child: controller.isLoading
 | 
			
		||||
                          ? const LinearProgressIndicator()
 | 
			
		||||
                          : Text(L10n.of(context)!.connect),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            Container(
 | 
			
		||||
              padding: const EdgeInsets.all(12),
 | 
			
		||||
              width: double.infinity,
 | 
			
		||||
              child: Hero(
 | 
			
		||||
                tag: 'loginButton',
 | 
			
		||||
                child: ElevatedButton(
 | 
			
		||||
                  onPressed: controller.isLoading
 | 
			
		||||
                      ? null
 | 
			
		||||
                      : controller.checkHomeserverAction,
 | 
			
		||||
                  child: controller.isLoading
 | 
			
		||||
                      ? const LinearProgressIndicator()
 | 
			
		||||
                      : Text(L10n.of(context)!.connect),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -15,8 +15,7 @@ class LoginView extends StatelessWidget {
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return LoginScaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading:
 | 
			
		||||
            controller.loading ? null : const BackButton(color: Colors.white),
 | 
			
		||||
        leading: controller.loading ? null : const BackButton(),
 | 
			
		||||
        automaticallyImplyLeading: !controller.loading,
 | 
			
		||||
        centerTitle: true,
 | 
			
		||||
        title: Text(
 | 
			
		||||
@ -25,7 +24,6 @@ class LoginView extends StatelessWidget {
 | 
			
		||||
              .homeserver
 | 
			
		||||
              .toString()
 | 
			
		||||
              .replaceFirst('https://', '')),
 | 
			
		||||
          style: const TextStyle(color: Colors.white),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
      body: Builder(builder: (context) {
 | 
			
		||||
@ -49,10 +47,7 @@ class LoginView extends StatelessWidget {
 | 
			
		||||
                    errorText: controller.usernameError,
 | 
			
		||||
                    errorStyle: const TextStyle(color: Colors.orange),
 | 
			
		||||
                    hintText: L10n.of(context)!.emailOrUsername,
 | 
			
		||||
                    fillColor: Theme.of(context)
 | 
			
		||||
                        .colorScheme
 | 
			
		||||
                        .background
 | 
			
		||||
                        .withOpacity(0.75),
 | 
			
		||||
                    fillColor: Theme.of(context).colorScheme.background,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
@ -82,10 +77,7 @@ class LoginView extends StatelessWidget {
 | 
			
		||||
                      onPressed: controller.toggleShowPassword,
 | 
			
		||||
                    ),
 | 
			
		||||
                    hintText: L10n.of(context)!.password,
 | 
			
		||||
                    fillColor: Theme.of(context)
 | 
			
		||||
                        .colorScheme
 | 
			
		||||
                        .background
 | 
			
		||||
                        .withOpacity(0.75),
 | 
			
		||||
                    fillColor: Theme.of(context).colorScheme.background,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
@ -94,6 +86,10 @@ class LoginView extends StatelessWidget {
 | 
			
		||||
                child: Padding(
 | 
			
		||||
                  padding: const EdgeInsets.all(12.0),
 | 
			
		||||
                  child: ElevatedButton(
 | 
			
		||||
                    style: ElevatedButton.styleFrom(
 | 
			
		||||
                      backgroundColor: Theme.of(context).colorScheme.primary,
 | 
			
		||||
                      foregroundColor: Theme.of(context).colorScheme.onPrimary,
 | 
			
		||||
                    ),
 | 
			
		||||
                    onPressed: controller.loading
 | 
			
		||||
                        ? null
 | 
			
		||||
                        : () => controller.login(context),
 | 
			
		||||
@ -103,36 +99,41 @@ class LoginView extends StatelessWidget {
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              Row(
 | 
			
		||||
                children: [
 | 
			
		||||
                  const Expanded(
 | 
			
		||||
              Padding(
 | 
			
		||||
                padding: const EdgeInsets.symmetric(horizontal: 16.0),
 | 
			
		||||
                child: Row(
 | 
			
		||||
                  children: [
 | 
			
		||||
                    Expanded(
 | 
			
		||||
                      child: Divider(
 | 
			
		||||
                    color: Colors.white,
 | 
			
		||||
                    thickness: 1,
 | 
			
		||||
                  )),
 | 
			
		||||
                  Padding(
 | 
			
		||||
                    padding: const EdgeInsets.all(12.0),
 | 
			
		||||
                    child: Text(
 | 
			
		||||
                      L10n.of(context)!.or,
 | 
			
		||||
                      style: const TextStyle(
 | 
			
		||||
                        color: Colors.white,
 | 
			
		||||
                        fontSize: 18,
 | 
			
		||||
                        thickness: 1,
 | 
			
		||||
                        color: Theme.of(context).textTheme.subtitle1?.color,
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                  const Expanded(
 | 
			
		||||
                    Padding(
 | 
			
		||||
                      padding: const EdgeInsets.all(12.0),
 | 
			
		||||
                      child: Text(
 | 
			
		||||
                        L10n.of(context)!.or,
 | 
			
		||||
                        style: const TextStyle(fontSize: 18),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                    Expanded(
 | 
			
		||||
                      child: Divider(
 | 
			
		||||
                    color: Colors.white,
 | 
			
		||||
                    thickness: 1,
 | 
			
		||||
                  )),
 | 
			
		||||
                ],
 | 
			
		||||
                        thickness: 1,
 | 
			
		||||
                        color: Theme.of(context).textTheme.subtitle1?.color,
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              Padding(
 | 
			
		||||
                padding: const EdgeInsets.all(12.0),
 | 
			
		||||
                child: ElevatedButton(
 | 
			
		||||
                  onPressed:
 | 
			
		||||
                      controller.loading ? () {} : controller.passwordForgotten,
 | 
			
		||||
                  style: ElevatedButton.styleFrom(foregroundColor: Colors.red),
 | 
			
		||||
                  style: ElevatedButton.styleFrom(
 | 
			
		||||
                    foregroundColor: Theme.of(context).colorScheme.error,
 | 
			
		||||
                    backgroundColor: Theme.of(context).colorScheme.onError,
 | 
			
		||||
                  ),
 | 
			
		||||
                  child: Text(L10n.of(context)!.passwordForgotten),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
 | 
			
		||||
@ -13,13 +13,9 @@ class SignupPageView extends StatelessWidget {
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return LoginScaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        leading:
 | 
			
		||||
            controller.loading ? null : const BackButton(color: Colors.white),
 | 
			
		||||
        leading: controller.loading ? null : const BackButton(),
 | 
			
		||||
        automaticallyImplyLeading: !controller.loading,
 | 
			
		||||
        title: Text(
 | 
			
		||||
          L10n.of(context)!.signUp,
 | 
			
		||||
          style: const TextStyle(color: Colors.white),
 | 
			
		||||
        ),
 | 
			
		||||
        title: Text(L10n.of(context)!.signUp),
 | 
			
		||||
      ),
 | 
			
		||||
      body: Form(
 | 
			
		||||
        key: controller.formKey,
 | 
			
		||||
@ -50,10 +46,7 @@ class SignupPageView extends StatelessWidget {
 | 
			
		||||
                  ),
 | 
			
		||||
                  errorStyle: const TextStyle(color: Colors.orange),
 | 
			
		||||
                  hintText: L10n.of(context)!.chooseAStrongPassword,
 | 
			
		||||
                  fillColor: Theme.of(context)
 | 
			
		||||
                      .colorScheme
 | 
			
		||||
                      .background
 | 
			
		||||
                      .withOpacity(0.75),
 | 
			
		||||
                  fillColor: Theme.of(context).colorScheme.background,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
@ -72,10 +65,7 @@ class SignupPageView extends StatelessWidget {
 | 
			
		||||
                    prefixIcon: const Icon(Icons.repeat_outlined),
 | 
			
		||||
                    hintText: L10n.of(context)!.repeatPassword,
 | 
			
		||||
                    errorStyle: const TextStyle(color: Colors.orange),
 | 
			
		||||
                    fillColor: Theme.of(context)
 | 
			
		||||
                        .colorScheme
 | 
			
		||||
                        .background
 | 
			
		||||
                        .withOpacity(0.75),
 | 
			
		||||
                    fillColor: Theme.of(context).colorScheme.background,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
@ -93,10 +83,8 @@ class SignupPageView extends StatelessWidget {
 | 
			
		||||
                  prefixIcon: const Icon(Icons.mail_outlined),
 | 
			
		||||
                  hintText: L10n.of(context)!.enterAnEmailAddress,
 | 
			
		||||
                  errorText: controller.error,
 | 
			
		||||
                  fillColor: Theme.of(context)
 | 
			
		||||
                      .colorScheme
 | 
			
		||||
                      .background
 | 
			
		||||
                      .withOpacity(0.75),
 | 
			
		||||
                  fillColor: Theme.of(context).colorScheme.background,
 | 
			
		||||
                  errorMaxLines: 4,
 | 
			
		||||
                  errorStyle: TextStyle(
 | 
			
		||||
                    color: controller.emailController.text.isEmpty
 | 
			
		||||
                        ? Colors.orangeAccent
 | 
			
		||||
@ -110,6 +98,10 @@ class SignupPageView extends StatelessWidget {
 | 
			
		||||
              child: Padding(
 | 
			
		||||
                padding: const EdgeInsets.all(12),
 | 
			
		||||
                child: ElevatedButton(
 | 
			
		||||
                  style: ElevatedButton.styleFrom(
 | 
			
		||||
                    foregroundColor: Theme.of(context).colorScheme.onPrimary,
 | 
			
		||||
                    backgroundColor: Theme.of(context).colorScheme.primary,
 | 
			
		||||
                  ),
 | 
			
		||||
                  onPressed: controller.loading ? () {} : controller.signup,
 | 
			
		||||
                  child: controller.loading
 | 
			
		||||
                      ? const LinearProgressIndicator()
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,7 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter/services.dart';
 | 
			
		||||
 | 
			
		||||
import 'package:fluffychat/config/app_config.dart';
 | 
			
		||||
import 'package:fluffychat/config/themes.dart';
 | 
			
		||||
 | 
			
		||||
class LoginScaffold extends StatelessWidget {
 | 
			
		||||
  final Widget body;
 | 
			
		||||
@ -13,32 +15,47 @@ class LoginScaffold extends StatelessWidget {
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        automaticallyImplyLeading: appBar?.automaticallyImplyLeading ?? true,
 | 
			
		||||
        centerTitle: appBar?.centerTitle,
 | 
			
		||||
        title: appBar?.title,
 | 
			
		||||
        leading: appBar?.leading,
 | 
			
		||||
        actions: appBar?.actions,
 | 
			
		||||
        iconTheme: const IconThemeData(color: Colors.white),
 | 
			
		||||
        elevation: 0,
 | 
			
		||||
        scrolledUnderElevation: 0,
 | 
			
		||||
        backgroundColor: Colors.transparent,
 | 
			
		||||
        systemOverlayStyle: SystemUiOverlayStyle.light,
 | 
			
		||||
      ),
 | 
			
		||||
      extendBodyBehindAppBar: true,
 | 
			
		||||
      extendBody: true,
 | 
			
		||||
      body: Container(
 | 
			
		||||
        decoration: const BoxDecoration(
 | 
			
		||||
          image: DecorationImage(
 | 
			
		||||
            fit: BoxFit.cover,
 | 
			
		||||
            image: AssetImage('assets/login_wallpaper.png'),
 | 
			
		||||
          ),
 | 
			
		||||
    final isMobileMode = !FluffyThemes.isColumnMode(context);
 | 
			
		||||
    return Container(
 | 
			
		||||
      decoration: const BoxDecoration(
 | 
			
		||||
        image: DecorationImage(
 | 
			
		||||
          image: AssetImage('assets/login_wallpaper.png'),
 | 
			
		||||
          fit: BoxFit.cover,
 | 
			
		||||
        ),
 | 
			
		||||
        alignment: Alignment.center,
 | 
			
		||||
        child: ConstrainedBox(
 | 
			
		||||
          constraints: const BoxConstraints(maxWidth: 480),
 | 
			
		||||
          child: body,
 | 
			
		||||
      ),
 | 
			
		||||
      child: Center(
 | 
			
		||||
        child: Material(
 | 
			
		||||
          color: Theme.of(context).brightness == Brightness.light
 | 
			
		||||
              ? Theme.of(context).scaffoldBackgroundColor.withOpacity(0.9)
 | 
			
		||||
              : Theme.of(context).scaffoldBackgroundColor.withOpacity(0.75),
 | 
			
		||||
          borderRadius: isMobileMode
 | 
			
		||||
              ? null
 | 
			
		||||
              : BorderRadius.circular(AppConfig.borderRadius),
 | 
			
		||||
          elevation: isMobileMode ? 0 : 10,
 | 
			
		||||
          clipBehavior: Clip.hardEdge,
 | 
			
		||||
          shadowColor: Colors.black,
 | 
			
		||||
          child: ConstrainedBox(
 | 
			
		||||
            constraints: isMobileMode
 | 
			
		||||
                ? const BoxConstraints()
 | 
			
		||||
                : const BoxConstraints(maxWidth: 480, maxHeight: 640),
 | 
			
		||||
            child: Scaffold(
 | 
			
		||||
              backgroundColor: Colors.transparent,
 | 
			
		||||
              appBar: appBar == null
 | 
			
		||||
                  ? null
 | 
			
		||||
                  : AppBar(
 | 
			
		||||
                      automaticallyImplyLeading:
 | 
			
		||||
                          appBar?.automaticallyImplyLeading ?? true,
 | 
			
		||||
                      centerTitle: appBar?.centerTitle,
 | 
			
		||||
                      title: appBar?.title,
 | 
			
		||||
                      leading: appBar?.leading,
 | 
			
		||||
                      actions: appBar?.actions,
 | 
			
		||||
                      backgroundColor: Colors.transparent,
 | 
			
		||||
                    ),
 | 
			
		||||
              extendBodyBehindAppBar: true,
 | 
			
		||||
              extendBody: true,
 | 
			
		||||
              body: body,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user