// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:async'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'marquee.dart'; /// Route names. (See [main] for more details.) /// /// The route names must match those sent by the platform-specific component. const String greenMarqueeRouteName = 'marquee_green'; const String purpleMarqueeRouteName = 'marquee_purple'; const String fullscreenRouteName = 'full'; const String hybridRouteName = 'hybrid'; /// Channel used to let the Flutter app know to reset the app to a specific /// route. See the [run] method. /// /// Note that we shouldn't use the `setInitialRoute` method on the system /// navigation channel, as that never gets propagated back to Flutter after the /// initial call. const String _kReloadChannelName = 'reload'; const BasicMessageChannel<String> _kReloadChannel = BasicMessageChannel<String>(_kReloadChannelName, StringCodec()); void main() { // Ensures bindings are initialized before doing anything. WidgetsFlutterBinding.ensureInitialized(); // Start listening immediately for messages from the iOS side. ObjC calls // will be made to let us know when we should be changing the app state. _kReloadChannel.setMessageHandler(run); // Start off with whatever the initial route is supposed to be. run(ui.window.defaultRouteName); } Future<String> run(String? name) async { // The platform-specific component will call [setInitialRoute] on the Flutter // view (or view controller for iOS) to set [ui.window.defaultRouteName]. // We then dispatch based on the route names to show different Flutter // widgets. // Since we don't really care about Flutter-side navigation in this app, we're // not using a regular routes map. name ??= ''; switch (name) { case greenMarqueeRouteName: runApp(Marquee(color: Colors.green[400])); break; case purpleMarqueeRouteName: runApp(Marquee(color: Colors.purple[400])); break; case fullscreenRouteName: case hybridRouteName: default: runApp(FlutterView(initialRoute: name)); break; } return ''; } class FlutterView extends StatelessWidget { const FlutterView({required this.initialRoute}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter View', home: MyHomePage(initialRoute: initialRoute), ); } final String initialRoute; } class MyHomePage extends StatefulWidget { const MyHomePage({required this.initialRoute}); @override _MyHomePageState createState() => _MyHomePageState(); final String initialRoute; /// Whether we should display the home page in fullscreen mode. /// /// If in full screen mode, we will use an [AppBar] widget to show our own /// title. bool get isFullscreen => initialRoute == fullscreenRouteName; /// Whether tapping the Flutter button should notify an external source. /// /// If false, the button will increments our own internal counter. bool get hasExternalTarget => initialRoute == hybridRouteName; } class _MyHomePageState extends State<MyHomePage> { // The name of the message channel used to communicate with the // platform-specific component. // // This string must match the one used on the platform side. static const String _channel = 'increment'; // The message to send to the platform-specific component when our button // is tapped. static const String _pong = 'pong'; // Used to pass messages between the platform-specific component and the // Flutter component. static const BasicMessageChannel<String> _platform = BasicMessageChannel<String>(_channel, StringCodec()); // An internal count. Normally this represents the number of times that the // button on the Flutter page has been tapped. int _counter = 0; @override void initState() { super.initState(); _platform.setMessageHandler(_handlePlatformIncrement); } /// Directly increments our internal counter and rebuilds the UI. void _incrementCounter() { setState(() { _counter++; }); } /// Callback for messages sent by the platform-specific component. /// /// Increments our internal counter. Future<String> _handlePlatformIncrement(String? message) async { // Normally we'd dispatch based on the value of [message], but in this // sample, there is only one message that is sent to us. _incrementCounter(); return ''; } /// Sends a message to the platform-specific component to increment its /// counter. void _sendFlutterIncrement() { _platform.send(_pong); } @override Widget build(BuildContext context) { final String buttonName = widget.hasExternalTarget ? 'Platform button' : 'Button'; return Scaffold( appBar: widget.isFullscreen ? AppBar(title: const Text('Fullscreen Flutter')) : null, body: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Center( child: Text( '$buttonName tapped $_counter time${_counter == 1 ? '' : 's'}.', style: const TextStyle(fontSize: 17.0), ), ), const TextButton( child: Text('POP'), onPressed: SystemNavigator.pop, ), ], ), ), Container( padding: const EdgeInsets.only(bottom: 15.0, left: 5.0), child: Row( children: const <Widget>[ Text('Flutter', style: TextStyle(fontSize: 30.0)), ], ), ), ], ), floatingActionButton: Semantics( label: 'Increment via Flutter', child: FloatingActionButton( onPressed: widget.hasExternalTarget ? _sendFlutterIncrement : _incrementCounter, child: const Icon(Icons.add), ), ), ); } }