Skip to content

Textfield with FocusNode Rendered within an iFrame on Web Refuses to Lose Focus #155265

@caleb-at-pieces

Description

@caleb-at-pieces

Steps to reproduce

  1. Create a flutter app with the following main.dart file (this is just a hello world flutter app that contains a textfield along with a focusnode):
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  // Declare the FocusNode
  late FocusNode _focusNode;

  @override
  void initState() {
    super.initState();
    // Initialize the FocusNode
    _focusNode = FocusNode();
  }

  @override
  void dispose() {
    // Dispose of the FocusNode
    _focusNode.dispose();
    super.dispose();
  }

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
            const SizedBox(height: 20), // Add spacing
            // Add the TextField widget
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 40.0),
              child: TextField(
                focusNode: _focusNode,
                decoration: const InputDecoration(
                  labelText: 'Enter text',
                  border: OutlineInputBorder(),
                ),
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _incrementCounter();
          // Request focus when the button is pressed
          _focusNode.requestFocus();
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}
  1. Build the app with flutter build web
  2. Run this dart file to statically serve the flutter web application on port 3000:
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_plus/shelf_plus.dart' show Pipeline;
import 'package:shelf_static/shelf_static.dart';
import 'package:shelf/shelf.dart';

Map<String, String> corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': '*',
};

Middleware cors = createMiddleware(responseHandler: (response) {
  return response.change(headers: corsHeaders);
}, requestHandler: (request) {
  if (request.method == 'OPTIONS') {
    return Response.ok(null, headers: corsHeaders);
  }
  return null;
});

void main() async {
  Pipeline pipeline = Pipeline();

  // pipeline = pipeline.addMiddleware(shelf.logRequests());
  pipeline = pipeline.addMiddleware(cors);

  var server = await io.serve(
    pipeline.addHandler(createStaticHandler('build/web', defaultDocument: 'index.html')),
    'localhost',
    3000,
    poweredByHeader: '',
  );

  /// This will remove default headers from the server.
  server.defaultResponseHeaders.removeAll('X-Frame-Options');
  // server.defaultResponseHeaders.add('X-Frame-Options', 'null');
}
  1. open this html file in your browser (this simply opens up an iframe to localhost:3000)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Iframe Example</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
        }
        iframe {
            width: 50vw; /* 50% of the viewport width */
            height: 100vh; /* 100% of the viewport height */
            border: none; /* Remove default border */
        }
    </style>
</head>
<body>
	<input placeholder="normal browser text input">
    <iframe src="http://localhost:3000"></iframe>
</body>
</html>
  1. click on the flutter app's TextField input widget
  2. Attempt to click on the browser input with the placeholder 'normal browser text input'
  3. The flutter TextField will momentarily lose focus, to only request it again, making it impossible to type into the other normal text input without clicking outside of the TextField in the flutter application

Expected results

I would expect that if you click outside of an iFrame that is a flutter web application, a focused TextField should properly lose focus

Actual results

The Flutter TextField does not lose focus until you click outside of the TextField within the flutter application

Code sample

Code sample

I provided the code sample earlier

Screenshots or Video

Screenshots / Video demonstration
Screen.Recording.2024-09-16.at.2.00.33.PM.mov

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.24.3, on macOS 14.3.1 23D60 darwin-arm64, locale en-US)
[!] Android toolchain - develop for Android devices (Android SDK version 33.0.2)
    ✗ cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    ✗ Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/to/macos-android-setup for more details.
[!] Xcode - develop for iOS and macOS (Xcode 15.3)
    ✗ Unable to get list of installed Simulator runtimes.
[✓] Chrome - develop for the web
[✓] Android Studio (version 2022.2)
[✓] IntelliJ IDEA Ultimate Edition (version 2024.2.1)
[✓] IntelliJ IDEA Community Edition (version 2023.3.4)
[✓] VS Code (version 1.93.0)
[✓] Connected device (3 available)
[✓] Network resources

! Doctor found issues in 2 categories.

Metadata

Metadata

Labels

P1High-priority issues at the top of the work lista: text inputEntering text in a text field or keyboard related problemscustomer: googleVarious Google teamsf: focusFocus traversal, gaining or losing focusfound in release: 3.24Found to occur in 3.24found in release: 3.26Found to occur in 3.26frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-webWeb applications specificallyr: fixedIssue is closed as already fixed in a newer versionteam-webOwned by Web platform teamtriaged-webTriaged by Web platform teamworkaround availableThere is a workaround available to overcome the issue

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions