0

When highlighting texts in Textfield => It shows two highlights and when scrolling, the other highlights remain at the same position

When highlighting texts: When highlighting texts

When scrolling: When scrolling

This only happens on Flutter web, so I guess the reason is that the different behavior between flutter Canvas and web DOM.

  • Environment

    • Flutter 3.38.2 • channel stable • https://github.com/flutter/flutter.git

      Framework • revision f5a8537f90 (5 days ago) • 2025年11月18日 09:27:21 -0500

      Engine • hash 78c3c9557e50ee7c676fa37562558c59efd8406a (revision b5990e5ccc) (10 days ago) •

      2025年11月12日 21:08:24.000Z

      Tools • Dart 3.10.0 • DevTools 2.51.1

    • Safari on iOS simulator ( iPhone 13 Pro, iOS 18.6 )

    • Chrome on iPhone 13 Pro Max, iOS 18.7.2

  • Related Codes

    • main.dart
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart' show kIsWeb, kReleaseMode;
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'firebase_options.dart' as prod;
import 'firebase_options_stg.dart' as stg;
import 'firebase_emulator.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'l10n/app_localizations.dart';
import 'client/screens/start_page.dart';
import 'client/screens/login_page.dart';
import 'client/screens/signup_page.dart';
import 'client/screens/questions_page.dart';
import 'client/screens/home_page.dart';
import 'auth_required.dart';
import 'client/screens/plan_proposal_page.dart';
import 'client/screens/settings_page.dart';
import 'client/screens/weight_history_screen.dart';
import 'coach/screens/coach_dashboard_page.dart';
import 'coach/screens/coach_login_page.dart';
import 'utils/auth_redirect_wrapper.dart';
void main() async {
 WidgetsFlutterBinding.ensureInitialized();
 // Select Firebase options by build-time environment.
 // - Debug/Profile: default to "stg" unless FIREBASE_ENV is explicitly provided
 // - Release: default to "prod" unless FIREBASE_ENV is explicitly provided
 const String envDefine = String.fromEnvironment(
 'FIREBASE_ENV',
 defaultValue: '',
 );
 final String env = envDefine.isEmpty
 ? (kReleaseMode ? 'prod' : 'stg')
 : envDefine;
 final FirebaseOptions firebaseOptions = env == 'stg'
 ? stg.DefaultFirebaseOptions.currentPlatform
 : prod.DefaultFirebaseOptions.currentPlatform;
 await Firebase.initializeApp(options: firebaseOptions);
 await connectFirebaseToEmulatorIfNeeded();
 runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
 const MyApp({super.key});
 @override
 Widget build(BuildContext context) {
 return MaterialApp(
 builder: (context, child) {
 const bool useEmulator = bool.fromEnvironment(
 'USE_FIREBASE_EMULATOR',
 // Default to emulator in non-release builds for safety
 defaultValue: !kReleaseMode,
 );
 final double bottom = (kIsWeb && useEmulator) ? 40.0 : 0.0;
 if (bottom == 0.0) return child!;
 return Padding(
 padding: EdgeInsets.only(bottom: bottom),
 child: child,
 );
 },
 onGenerateTitle: (context) => AppLocalizations.of(context)!.appTitle,
 theme: () {
 const double webLineHeight = 1.3;
 final baseTheme = ThemeData(
 colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
 fontFamily: 'NotoSansJP',
 );
 final TextTheme baseTextTheme = baseTheme.textTheme;
 final TextTheme adjustedTextTheme = kIsWeb
 ? baseTextTheme.copyWith(
 // Fix line height for all text styles on web to align browser and Flutter selection highlights
 displayLarge: baseTextTheme.displayLarge?.copyWith(
 height: webLineHeight,
 ),
 displayMedium: baseTextTheme.displayMedium?.copyWith(
 height: webLineHeight,
 ),
 displaySmall: baseTextTheme.displaySmall?.copyWith(
 height: webLineHeight,
 ),
 headlineLarge: baseTextTheme.headlineLarge?.copyWith(
 height: webLineHeight,
 ),
 headlineMedium: baseTextTheme.headlineMedium?.copyWith(
 height: webLineHeight,
 ),
 headlineSmall: baseTextTheme.headlineSmall?.copyWith(
 height: webLineHeight,
 ),
 titleLarge: baseTextTheme.titleLarge?.copyWith(
 height: webLineHeight,
 ),
 titleMedium: baseTextTheme.titleMedium?.copyWith(
 height: webLineHeight,
 ),
 titleSmall: baseTextTheme.titleSmall?.copyWith(
 height: webLineHeight,
 ),
 bodyLarge: baseTextTheme.bodyLarge?.copyWith(
 height: webLineHeight,
 ),
 bodyMedium: baseTextTheme.bodyMedium?.copyWith(
 height: webLineHeight,
 ),
 bodySmall: baseTextTheme.bodySmall?.copyWith(
 height: webLineHeight,
 ),
 labelLarge: baseTextTheme.labelLarge?.copyWith(
 height: webLineHeight,
 ),
 labelMedium: baseTextTheme.labelMedium?.copyWith(
 height: webLineHeight,
 ),
 labelSmall: baseTextTheme.labelSmall?.copyWith(
 height: webLineHeight,
 ),
 )
 : baseTextTheme;
 return baseTheme.copyWith(
 textTheme: adjustedTextTheme,
 primaryTextTheme: adjustedTextTheme,
 );
 }(),
 localizationsDelegates: [
 GlobalMaterialLocalizations.delegate,
 GlobalWidgetsLocalizations.delegate,
 GlobalCupertinoLocalizations.delegate,
 AppLocalizations.delegate,
 ],
 supportedLocales: AppLocalizations.supportedLocales,
 initialRoute: '/start',
 routes: {
 '/start': (_) => const AuthRedirectWrapper(child: StartPage()),
 '/login': (_) => const AuthRedirectWrapper(child: LoginPage()),
 '/signup': (_) => const SignUpPage(),
 '/questions': (_) => const QuestionsPage(),
 '/home': (_) =>
 const AuthRequired(requiredRole: 'client', child: HomePage()),
 '/planProposal': (_) => const PlanProposalPage(),
 '/settings': (_) =>
 const AuthRequired(requiredRole: 'client', child: SettingsPage()),
 '/weightHistory': (_) => const AuthRequired(
 requiredRole: 'client',
 child: WeightHistoryScreen(),
 ),
 '/coach/login': (_) =>
 const AuthRedirectWrapper(child: CoachLoginPage()),
 '/coach/dashboard': (_) => const AuthRequired(
 requiredRole: 'coach',
 child: CoachDashboardPage(),
 ),
 },
 );
 }
}
  • web/index.html
<!DOCTYPE html>
<html>
<head>
 <!--
 If you are serving your web app in a path other than the root, change the
 href value below to reflect the base path you are serving from.
 The path provided below has to start and end with a slash "/" in order for
 it to work correctly.
 For more details:
 * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
 This is a placeholder for base href that will be replaced by the value of
 the `--base-href` argument provided to `flutter build`.
 -->
 <base href="$FLUTTER_BASE_HREF">
 <meta charset="UTF-8">
 <meta content="IE=Edge" http-equiv="X-UA-Compatible">
 <meta name="description" content="A new Flutter project.">
 <!-- iOS meta tags & icons -->
 <meta name="mobile-web-app-capable" content="yes">
 <meta name="apple-mobile-web-app-status-bar-style" content="black">
 <meta name="apple-mobile-web-app-title" content="nutrition_coaching_app">
 <link rel="apple-touch-icon" href="icons/Icon-192.png">
 <!-- Favicon -->
 <link rel="icon" type="image/png" href="favicon.png"/>
 <title>nutrition_coaching_app</title>
 <link rel="manifest" href="manifest.json">
</head>
<body>
 <script src="flutter_bootstrap.js" async></script>
</body>
</html>
  • Textfield section where the issue is happening
TextField(
 controller: _controller,
 maxLines: null,
 decoration: const InputDecoration(border: OutlineInputBorder()),
 style: Theme.of(context).textTheme.bodyMedium,
),

Note: I've tried the following 2 ways to add to <head> section in index.html, but neither of them worked

  1. <style>
    /* Keep Flutter highlight; hide browser selection for hidden editing element */
    .flt-text-editing::selection {
    background-color: transparent !important;
    color: transparent !important; /* Hide text, keep caret logic working */
    }
    .flt-text-editing::-moz-selection {
    background-color: transparent !important;
    color: transparent !important;
    }
    </style>

  2. \<style\> 
     /\* Align hidden editing element font metrics with Flutter TextField \*/ 
     .flt-text-editing { 
     font-family: "NotoSansJP", system-ui, -apple-system, BlinkMacSystemFont, 
     "Segoe UI", sans-serif !important; 
     font-size: 14px !important; /\* Match bodyMedium default fontSize \*/ 
     line-height: 1.3 !important; /\* Match TextStyle.height: 1.3 \*/ 
     letter-spacing: 0 !important; /\* Default letter spacing \*/ 
     } 
    \</style\>
    
Frank van Puffelen
603k85 gold badges895 silver badges868 bronze badges
asked 2 days ago
New contributor
shoho is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.