is_tv_ffi 0.7.1
is_tv_ffi: ^0.7.1 copied to clipboard
Detects whether a Flutter app is running on a TV: Android TV, tvOS, webOS, Tizen and TV-mode Linux and Windows, using FFI and JNI rather than platform channels.
is_tv_ffi #
A Flutter plugin to detect if the current device is a TV.
Platform Support #
| Android | iOS | MacOS | Web | Linux | Windows |
|---|---|---|---|---|---|
| ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Installation #
Add this to your package's pubspec.yaml file:
dependencies:
is_tv_ffi: ^0.7.1
Usage #
import 'package:is_tv_ffi/is_tv_ffi.dart';
// Check if the device is a TV
final isTv = const IsTvFfi().isTv;
if (isTv) {
print('Running on a TV');
} else {
print('Not running on a TV');
}
isTv is a synchronous native call, so there is nothing to await.
iOS and macOS: Swift Package Manager only #
The iOS and macOS implementations ship as Swift packages (ios/is_tv_ffi/, macos/is_tv_ffi/) with no podspec, so they require Flutter 3.44 or newer, where Swift Package Manager is enabled by default.
If Swift Package Manager is disabled, your build stops with:
The following plugin(s) are only compatible with Swift Package Manager:
- is_tv_ffi
To fix it, re-enable Swift Package Manager:
flutter config --enable-swift-package-manager
Make sure it is not disabled per-project either — check for this in your app's pubspec.yaml and remove it if present:
flutter:
config:
enable-swift-package-manager: false
Desktop Usage #
For Linux and Windows, the plugin determines if it's a TV environment by checking for specific environment variables. This allows you to force "TV Mode" for your application when running on a media center PC, or for testing.
Forcing TV Mode #
Before running your app, set the following environment variable:
- Variable:
FLUTTER_IS_TV - Value:
1,true,yesoron(matched case-insensitively)
Linux / macOS Terminal
FLUTTER_IS_TV=1 flutter run
Windows PowerShell
$env:FLUTTER_IS_TV="1"
flutter run -d windows
Windows Command Prompt
set FLUTTER_IS_TV=1
flutter run -d windows
On Linux the plugin additionally recognises Steam Big Picture and SteamOS gaming sessions. Windows has no such heuristic: FLUTTER_IS_TV is the only signal it uses.
Implementation Details #
Android #
Uses UiModeManager to check if the current OS is Android TV, falling back to the leanback system feature for TV boxes that report a non-television UI mode.
fun isTv(context: Context): Boolean {
val uiModeManager = context.getSystemService(Context.UI_MODE_SERVICE) as? UiModeManager
if (uiModeManager?.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
return true
}
return context.packageManager?.hasSystemFeature(PackageManager.FEATURE_LEANBACK) == true
}
iOS / tvOS #
Uses UIDevice to check if the current device's user interface idiom is .tv.
public func isTV() -> Bool {
return device.userInterfaceIdiom == .tv
}
macOS #
Always returns false. macOS never reports a TV user interface idiom; the native symbol exists so that the FFI surface stays uniform across Apple platforms.
public func isTV() -> Bool {
return false
}
Linux #
Checks the FLUTTER_IS_TV environment variable, then the variables that indicate a Steam Big Picture or SteamOS gaming session. Which of those is set depends on the distribution and compositor, so several are checked.
bool is_tv() {
return IsTruthy(std::getenv("FLUTTER_IS_TV")) || IsSteamBigPictureSession();
}
bool IsSteamBigPictureSession() {
const char* session_desktop = std::getenv("XDG_SESSION_DESKTOP");
const char* current_desktop = std::getenv("XDG_CURRENT_DESKTOP");
return EqualsIgnoreCase(session_desktop, "steam") ||
EqualsIgnoreCase(session_desktop, "steamos") ||
EqualsIgnoreCase(current_desktop, "gamescope") ||
IsTruthy(std::getenv("SteamDeck"));
}
Windows #
Checks the FLUTTER_IS_TV environment variable.
bool is_tv() {
char* env_value = nullptr;
size_t len = 0;
if (_dupenv_s(&env_value, &len, "FLUTTER_IS_TV") != 0) {
return false;
}
const bool result = IsTruthy(env_value);
free(env_value);
return result;
}
Web #
Checks for the JavaScript globals that smart-TV runtimes inject (tizen, webOS, webOSSystem) — these are harder to spoof than a user agent — then falls back to matching the user agent against a list of TV keywords and patterns.
bool isTv() =>
_hasTvPlatformGlobal() || matchesTvUserAgent(window.navigator.userAgent);
static bool matchesTvUserAgent(String userAgent) {
final normalized = userAgent.toLowerCase();
return _tvPatterns.any((pattern) => pattern.hasMatch(normalized)) ||
_tvKeywords.any(normalized.contains);
}
The keyword list covers webOS, Tizen, Google TV, Chromecast, Fire TV, Apple TV, Bravia, VIDAA, Roku, HbbTV, NetCast, Net TV, CE-HTML and Opera TV. Patterns are used where a bare substring would misfire — Fire TV model codes (AFTB, AFTMM) are matched as whole words so that ordinary user agents containing "aft" are not treated as TVs.
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
License #
This project is licensed under the MIT License.