Problem
Flutter web apps on iOS Safari exhibit a "double selection" bug where text selection creates two overlapping selection layers, causing visual artifacts and
interaction issues.
Root Cause
iOS Safari creates both native browser text selection AND Flutter's custom SelectionArea selection simultaneously, resulting in conflicting selection states.
Working Workaround
HTML Solution (web/index.html)
<head>
<style>
* {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
/* Disable caret to prevent selection artifacts */
caret-color: rgba(255, 255, 255, 0) !important;
}
</style>
</head>
<body oncontextmenu="event.preventDefault();" >
...
Flutter Integration
// In main.dart or app initialization
import 'package:flutter/gestures.dart';
void main() {
// Disable browser context menu to let Flutter handle selection
if (kIsWeb) {
BrowserContextMenu.disableContextMenu();
}
runApp(MyApp());
}
How It Works
- Disables native selection - user-select: none prevents Safari's text selection
- Blocks touch events - Prevents iOS touch selection gestures
- Maintains Flutter selection - BrowserContextMenu.disableContextMenu() allows Flutter's SelectionArea to work
- Hides caret artifacts - caret-color: transparent eliminates visual glitches
Trade-offs
- ❌ Breaks native web text selection outside Flutter widgets
- ❌ May affect accessibility tools
- ✅ Provides consistent cross-platform selection behavior
- ✅ Eliminates iOS-specific double selection bug
Status
This workaround is recommended for Flutter web apps requiring text selection on iOS Safari until the official framework fix is released.
Deploying a Flutter web application to GitHub Pages is a straightforward process, but integrating a custom domain can sometimes introduce challenges. Recently, I faced an issue where my Flutter web app, which deployed perfectly to the default GitHub Pages URL, stopped working after setting up a custom subdomain. Here's a step-by-step guide on how I resolved this issue, which might help others facing the same problem.
The Initial Setup
I had a Flutter web app named "HappyNotes" hosted on GitHub Pages. The GitHub Actions workflow used to build and deploy the app looked like this:
name: Deploy HappyNotes Web
on:
workflow_dispatch:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.22.x'
channel: 'stable'
- name: Build web
run: |
cp .env.production .env
flutter config --enable-web
flutter build web --release --base-href "/HappyNotes/"
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.RELEASE_TOKEN }}
publish_dir: ./build/web
This workflow worked flawlessly with the default URL: https://shukebeta.github.io/HappyNotes
.
The Problem
After setting up a custom subdomain happynotes.shukebeta.com
, the app stopped working. The root cause of this issue involved multiple configuration steps that needed to be adjusted for the custom domain to work properly.
The Solution
Here’s how I resolved the issue:
1. DNS Settings
First, I ensured that the DNS settings were correctly configured:
-
DNS Provider Configuration:
- Added a CNAME record for
happynotes.shukebeta.com
pointing to shukebeta.github.io.
(attention: the last .
after io
is important!)
2. GitHub Pages Configuration
Next, I checked the GitHub Pages settings:
-
Custom Domain Setup:
- Navigated to the repository’s settings on GitHub.
- Under the "Pages" section, set the custom domain to
happynotes.shukebeta.com
.
- Enabled "Enforce HTTPS".
3. CNAME File
To ensure GitHub Pages recognized the custom domain, a CNAME
file will be needed to put into the build/web
directory. I automated this step in the GitHub Actions workflow:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.22.x'
channel: 'stable'
- name: Build web
run: |
cp .env.production .env
flutter config --enable-web
flutter build web --release --base-href "/"
- name: Create CNAME file
run: echo 'happynotes.shukebeta.com' > ./build/web/CNAME
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.RELEASE_TOKEN }}
publish_dir: ./build/web
4. Base URL Adjustment
Since the custom subdomain serves the app from the root, the base-href
parameter is also needed to adjust:
- name: Build web
run: |
cp .env.production .env
flutter config --enable-web
flutter build web --release --base-href "/"
that's it.