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.
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.
I updated flutter from 3.16 to 3.22 and it solved the issue