Search code examples
flutter

Not able to change selection handle color in flutter


So i have a problem. I have created my themes set them and it looks like the colors of cursor and selection are correct. Unfortunately for some unknown reason i am not able to change selection handler color.

enter image description here

flutter config:

Flutter 3.16.4 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 2e9cb0aa71 (2 months ago) • 2023-12-11 14:35:13 -0700
Engine • revision 54a7145303
Tools • Dart 3.2.3 • DevTools 2.28.4

Here is my main:

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:personal_assistant/src/features/authentication/screens/authentication_screen.dart';
import 'package:personal_assistant/src/features/chat/screens/chat_screen.dart';
import 'utils/theme/theme.dart';
import 'package:personal_assistant/src/extensions/sizes.dart';
import 'package:intl/intl.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

void main() {
  HttpOverrides.global = MyHttpOverrides();
  runApp(const MaterialApp(
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      home: MyApp()));
}

class MyHttpOverrides extends HttpOverrides {
  @override
  HttpClient createHttpClient(SecurityContext? context) {
    return super.createHttpClient(context)
      ..badCertificateCallback =
          (X509Certificate cert, String host, int port) => true;
  }
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    ExtensionSizes.init(context);
    return MaterialApp(
      theme: AppTheme.lightTheme(),
      darkTheme: AppTheme.darkTheme(),
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      debugShowCheckedModeBanner: false,
      home: const LoginPage(),
      routes: {
        '/loginpage': (context) => const LoginPage(),
        '/chatpage': (context) => const ChatPage(),
      },
    );
  }
}

here is my themes:

import 'package:flutter/material.dart';
import 'package:personal_assistant/src/extensions/sizes.dart';
import 'package:personal_assistant/src/constants/sizes.dart';

class AppTheme {
  static _buildBorder(Color color) {
    return OutlineInputBorder(
        borderRadius:
            const BorderRadius.all(Radius.circular(ConstantSizes.borderSize)),
        borderSide: BorderSide(color: color, width: 1.0));
  }

  static ThemeData lightTheme() {
    return ThemeData(
      brightness: Brightness.light,
      scaffoldBackgroundColor: const Color(0xFFFFFFFF),
      primaryColor: const Color(0xFF00579D),
      primarySwatch: const MaterialColor(0xFF00579D, <int, Color>{
        50: Color(0xFFD5EDFA),
        100: Color(0xFFBDE4F7),
        200: Color(0xFF96CBE2),
        300: Color(0xFF64C3D5),
        400: Color(0xFF28B9DA),
        500: Color(0xFF0090C5),
        600: Color(0xFF2382BA),
        700: Color(0xFF0075B1),
        800: Color(0xFF00579D),
        900: Color(0xFF0D273E)
      }),
      textTheme: TextTheme(
        headlineLarge: TextStyle(
            color: const Color(0xFFFFFFFF),
            fontSize: ExtensionSizes.headlineLarge,
            fontFamily: 'Roboto',
            fontWeight: FontWeight.bold),
        bodyMedium: const TextStyle(
            color: Color(0xFF7f7f7f),
            fontSize: ConstantSizes.bodyMediumSize,
            fontFamily: 'Arial',
            fontWeight: FontWeight.normal),
        labelMedium: const TextStyle(
          color: Color(0xFFffffff), // Text color
          fontSize: 16, // Text size
          fontWeight: FontWeight.normal,
          fontFamily: 'Arial', // Text weight
        ),
        labelSmall: const TextStyle(
          color: Color(0xFFEF8300), // Text color
          fontSize: 16, // Text size
          fontWeight: FontWeight.normal,
          fontFamily: 'Arial', // Text weight
        ),
      ),
      inputDecorationTheme: InputDecorationTheme(
        enabledBorder: _buildBorder(const Color(0xFF00579D)),
        fillColor: const Color(0xFFFFFFFF),
        errorBorder: _buildBorder(const Color(0xFFEF8300)),
        focusedErrorBorder: _buildBorder(const Color(0xFFEF8300)),
        border: _buildBorder(const Color(0xFF00579D)),
        focusedBorder: _buildBorder(const Color(0xFF00579D)),
        disabledBorder: _buildBorder(const Color(0xFF00579D)),
        contentPadding: const EdgeInsets.all(16),
        floatingLabelBehavior: FloatingLabelBehavior.never,
        errorStyle: const TextStyle(
            color: Color(0xFFEF8300),
            fontSize: ConstantSizes.bodyMediumSize,
            fontFamily: 'Arial',
            fontWeight: FontWeight.normal),
        hintStyle: const TextStyle(
            color: Color(0xFF7f7f7f),
            fontSize: ConstantSizes.bodyMediumSize,
            fontFamily: 'Arial',
            fontWeight: FontWeight.normal),
      ),
      textSelectionTheme: const TextSelectionThemeData(
        cursorColor: Color(0xFF00579D),
        selectionColor: Color(0xFF0090C5),
        selectionHandleColor: Color(0xFF0090C5),
      ),
      elevatedButtonTheme: ElevatedButtonThemeData(
        style: ButtonStyle(
          backgroundColor: MaterialStateProperty.resolveWith<Color>(
            (states) => const Color(0xFF0090C5), // Background color
          ),
          overlayColor: MaterialStateProperty.resolveWith<Color>(
            (states) => const Color(0xFFBDE4F7), // Splash color
          ),
        ),
      ),
    );
  }

  static ThemeData darkTheme() {
    return ThemeData(
      brightness: Brightness.dark,
      scaffoldBackgroundColor: const Color(0xFF1d1d1b),
      primaryColor: const Color(0xFF7f7f7f),
      primarySwatch: const MaterialColor(0xFF00579D, <int, Color>{
        50: Color(0xFFD5EDFA),
        100: Color(0xFFBDE4F7),
        200: Color(0xFF96CBE2),
        300: Color(0xFF64C3D5),
        400: Color(0xFF28B9DA),
        500: Color(0xFF0090C5),
        600: Color(0xFF2382BA),
        700: Color(0xFF0075B1),
        800: Color(0xFF00579D),
        900: Color(0xFF0D273E)
      }),
      textTheme: TextTheme(
        headlineLarge: TextStyle(
            color: const Color(0xFFFFFFFF),
            fontSize: ExtensionSizes.headlineLarge,
            fontFamily: 'Roboto',
            fontWeight: FontWeight.bold),
        bodyMedium: const TextStyle(
            color: Color(0xFFFFFFFF),
            fontSize: ConstantSizes.bodyMediumSize,
            fontFamily: 'Arial',
            fontWeight: FontWeight.normal),
        labelMedium: const TextStyle(
          color: Color(0xFFffffff), // Text color
          fontSize: 16, // Text size
          fontWeight: FontWeight.normal,
          fontFamily: 'Arial', // Text weight
        ),
        labelSmall: const TextStyle(
          color: Color(0xFFEF8300), // Text color
          fontSize: 16, // Text size
          fontWeight: FontWeight.normal,
          fontFamily: 'Arial', // Text weight
        ),
      ),
      inputDecorationTheme: InputDecorationTheme(
        fillColor: const Color(0xFF7f7f7f),
        enabledBorder: _buildBorder(const Color(0xFF000000)),
        errorBorder: _buildBorder(const Color(0xFFEF8300)),
        focusedErrorBorder: _buildBorder(const Color(0xFFEF8300)),
        border: _buildBorder(const Color(0xFF000000)),
        focusedBorder: _buildBorder(const Color(0xFF000000)),
        disabledBorder: _buildBorder(const Color(0xFF000000)),
        contentPadding: const EdgeInsets.all(16),
        floatingLabelBehavior: FloatingLabelBehavior.never,
        errorStyle: const TextStyle(
            color: Color(0xFFEF8300),
            fontSize: ConstantSizes.bodyMediumSize,
            fontFamily: 'Arial',
            fontWeight: FontWeight.normal),
        hintStyle: const TextStyle(
            color: Color(0xFFfffffff),
            fontSize: ConstantSizes.bodyMediumSize,
            fontFamily: 'Arial',
            fontWeight: FontWeight.normal),
      ),
      textSelectionTheme: const TextSelectionThemeData(
        cursorColor: Color(0xFFffffff),
        selectionColor: Color(0xFFd9d9d9),
        selectionHandleColor: Color(0xFFd9d9d9),
      ),
      elevatedButtonTheme: ElevatedButtonThemeData(
        style: ButtonStyle(
          backgroundColor: MaterialStateProperty.resolveWith<Color>(
            (states) => const Color(0xFF7f7f7f), // Background color
          ),
          overlayColor: MaterialStateProperty.resolveWith<Color>(
            (states) => const Color(0xFFd9d9d9), // Splash color
          ),
        ),
      ),
    );
  }
}

Here is my authentication screen where i call my textFormField:

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  _LoginState createState() => _LoginState();
}

class _LoginState extends State<LoginPage> {
  TextEditingController usernameFieldController = TextEditingController();
  TextEditingController passwordFieldController = TextEditingController();
  double dynamicSizedBoxSize = 20;
  String errorMessage = "";
  bool obscureText = true;

  void authenticateUser() async {
    String username = usernameFieldController.text;
    String password = passwordFieldController.text;

    bool isAuthenticated = await AuthenticationController.authenticateUser(
        context, username, password);
    if (isAuthenticated && context.mounted) {
      Navigator.pushNamed(context, '/chatpage');
    } else {
      setState(() {
        errorMessage = "Username or password is invalid";
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.width;
    final logoSize = screenWidth / 1.5;

    final ThemeData theme = Theme.of(context);
    Brightness brightness = MediaQuery.of(context).platformBrightness;
    Color scaffoldBackgroundColor = brightness == Brightness.dark
        ? theme.scaffoldBackgroundColor
        : theme.primaryColor;

    print(theme.textSelectionTheme);
    Icon passwordIcon = Icon(
        obscureText ? Icons.visibility_off : Icons.visibility,
        color: theme.textTheme.bodyMedium?.color);

    return Scaffold(
      backgroundColor: scaffoldBackgroundColor,
      body: Center(
        child: Column(
          children: [
            Expanded(
              child: Center(
                child: SingleChildScrollView(
                  reverse: true,
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: [
                      Image.asset(
                        ImageStrings.logo,
                        width: logoSize,
                      ),
                      Text(
                        "Personal Assistant",
                        style: theme.textTheme.headlineLarge,
                      ),
                      SizedBox(
                        height: dynamicSizedBoxSize,
                      ),
                      SizedBox(
                          height: errorMessage.isEmpty
                              ? theme.textTheme.labelSmall?.fontSize
                              : 0), // Add this SizedBox
                      Visibility(
                        visible: errorMessage.isNotEmpty,
                        child: Text(
                          errorMessage,
                          style: theme.textTheme.labelSmall,
                        ),
                      ),
                      CustomTextField(
                        controller: usernameFieldController,
                        hintText: AppLocalizations.of(context)!.username,
                        obscureText: false,
                        minWidth: screenWidth / 1.2,
                        maxWidth: screenWidth / 1.2,
                        padding: const EdgeInsets.fromLTRB(
                            ConstantSizes.horizontalPadding,
                            ConstantSizes.verticalPadding,
                            ConstantSizes.horizontalPadding,
                            ConstantSizes.verticalPadding / 2),
                      ),
                      CustomTextField(
                        controller: passwordFieldController,
                        hintText: AppLocalizations.of(context)!.password,
                        minWidth: screenWidth / 1.2,
                        maxWidth: screenWidth / 1.2,
                        padding: const EdgeInsets.fromLTRB(
                            ConstantSizes.horizontalPadding,
                            ConstantSizes.verticalPadding / 2,
                            ConstantSizes.horizontalPadding,
                            ConstantSizes.verticalPadding / 2),
                        suffixIcon: IconButton(
                          onPressed: () {
                            setState(() {
                              obscureText = !obscureText;
                            });
                          },
                          icon: passwordIcon,
                        ),
                        obscureText: obscureText,
                      ),
                      CustomButton(
                        onPressed: () => authenticateUser(),
                        text: AppLocalizations.of(context)!.signIn,
                        padding: const EdgeInsets.fromLTRB(
                            ConstantSizes.horizontalPadding,
                            ConstantSizes.verticalPadding / 2,
                            ConstantSizes.horizontalPadding,
                            ConstantSizes.verticalPadding),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

and finally my CustomTextField:

class CustomTextField extends StatelessWidget {
  final TextEditingController controller;
  final String hintText;
  final bool obscureText;
  final double minWidth;
  final double maxWidth;
  final Function(String)? onSubmitted;
  final EdgeInsetsGeometry? padding;
  final IconButton? suffixIcon;

  const CustomTextField({
    super.key,
    required this.controller,
    required this.hintText,
    required this.obscureText,
    required this.minWidth,
    required this.maxWidth,
    this.onSubmitted,
    this.padding,
    this.suffixIcon,
  });

  @override
  Widget build(BuildContext context) {
    ThemeData theme = Theme.of(context);

    final inputDecoration = InputDecoration(
      hintText: hintText,
      filled: true,
      fillColor: theme.inputDecorationTheme.fillColor,
      border: theme.inputDecorationTheme.border,
      contentPadding: theme.inputDecorationTheme.contentPadding,
      hintStyle: theme.inputDecorationTheme.hintStyle,
      suffixIcon: suffixIcon,
    );
    print(Theme.of(context).textSelectionTheme.cursorColor);
    return Padding(
      padding: padding ??
          const EdgeInsets.symmetric(
              vertical: ConstantSizes.verticalPadding,
              horizontal: ConstantSizes.horizontalPadding),
      child: ConstrainedBox(
        constraints: BoxConstraints(
          minWidth: minWidth,
          maxWidth: maxWidth,
          minHeight: 25.0,
          maxHeight: 135.0,
        ),
        child: Scrollbar(
          child: TextFormField(
            style: theme.textTheme.bodyMedium,
            keyboardType: TextInputType.text,
            obscureText: obscureText,
            maxLines: 1,
            controller: controller,
            decoration: inputDecoration,
            textInputAction: TextInputAction.send,
            onFieldSubmitted: (text) => onSubmitted?.call(text),
          ),
        ),
      ),
    );
  }
}

I am a beginner at flutter so i understando that the code is not that good, im in the process of making it better, but got stuck with this little selection handler with the wrong color.

I tried wrapping with a Theme widget putting the color in the data parameter like suggested in the answer How to change selected text color locally - flutter but id didn´t work as well.

expecting: I was expecting the color of the selection handler to be a light blue in the lightTheme and light grey in the DarkTheme

result: Got a purple text selection handler.


Solution

  • I updated flutter from 3.16 to 3.22 and it solved the issue