snapshot of https://github.com/flutter/samples.git (main, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Main Branch CI / Test Flutter beta on ubuntu-latest (push) Has been skipped
Main Branch CI / Test Flutter stable on ubuntu-latest (push) Has been skipped
Main Branch CI / Test Flutter beta on macos-latest (push) Has been cancelled
Main Branch CI / Test Flutter stable on macos-latest (push) Has been cancelled
Some checks failed
Main Branch CI / Test Flutter beta on ubuntu-latest (push) Has been skipped
Main Branch CI / Test Flutter stable on ubuntu-latest (push) Has been skipped
Main Branch CI / Test Flutter beta on macos-latest (push) Has been cancelled
Main Branch CI / Test Flutter stable on macos-latest (push) Has been cancelled
This commit is contained in:
158
add_to_app/plugin/flutter_module_using_plugin/lib/cell.dart
Normal file
158
add_to_app/plugin/flutter_module_using_plugin/lib/cell.dart
Normal file
@@ -0,0 +1,158 @@
|
||||
// Copyright 2019 The Flutter team. 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:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:sensors_plus/sensors_plus.dart';
|
||||
|
||||
// This is on alternate entrypoint for this module to display Flutter UI in
|
||||
// a (multi-)view integration scenario.
|
||||
void main() {
|
||||
runApp(const Cell());
|
||||
}
|
||||
|
||||
class Cell extends StatefulWidget {
|
||||
const Cell({super.key});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _CellState();
|
||||
}
|
||||
|
||||
class _CellState extends State<Cell> with WidgetsBindingObserver {
|
||||
static const double gravity = 9.81;
|
||||
static final AccelerometerEvent defaultPosition = AccelerometerEvent(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
DateTime.now(),
|
||||
);
|
||||
|
||||
int cellNumber = 0;
|
||||
Random? _random;
|
||||
AppLifecycleState? appLifecycleState;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
const channel = MethodChannel('dev.flutter.example/cell');
|
||||
channel.setMethodCallHandler((call) async {
|
||||
if (call.method == 'setCellNumber') {
|
||||
setState(() {
|
||||
cellNumber = call.arguments as int;
|
||||
_random = Random(cellNumber);
|
||||
});
|
||||
}
|
||||
});
|
||||
// Keep track of what the current platform lifecycle state is.
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
setState(() {
|
||||
appLifecycleState = state;
|
||||
});
|
||||
}
|
||||
|
||||
// Show a random bright color.
|
||||
Color randomLightColor() {
|
||||
_random ??= Random(cellNumber);
|
||||
|
||||
return Color.fromARGB(
|
||||
255,
|
||||
_random!.nextInt(50) + 205,
|
||||
_random!.nextInt(50) + 205,
|
||||
_random!.nextInt(50) + 205,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
// The Flutter cells will be noticeably different (due to background color
|
||||
// and the Flutter logo). The banner breaks immersion.
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: Container(
|
||||
color: Colors.white,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return Card(
|
||||
// Mimic the platform Material look.
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 36,
|
||||
vertical: 24,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
elevation: 16,
|
||||
color: randomLightColor(),
|
||||
child: Stack(
|
||||
children: [
|
||||
Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
// Show a number provided by the platform based on
|
||||
// the cell's index.
|
||||
cellNumber.toString(),
|
||||
style: Theme.of(context).textTheme.displaySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
left: 42,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: Opacity(
|
||||
opacity: 0.2,
|
||||
child: StreamBuilder<AccelerometerEvent>(
|
||||
// Don't continuously rebuild for nothing when the
|
||||
// cell isn't visible.
|
||||
stream: appLifecycleState == AppLifecycleState.resumed
|
||||
? accelerometerEventStream()
|
||||
: Stream.value(defaultPosition),
|
||||
initialData: defaultPosition,
|
||||
builder: (context, snapshot) {
|
||||
final data = snapshot.data;
|
||||
if (data == null) {
|
||||
return const CircularProgressIndicator.adaptive();
|
||||
}
|
||||
return Transform(
|
||||
// Figure out the phone's orientation relative
|
||||
// to gravity's direction. Ignore the z vector.
|
||||
transform:
|
||||
Matrix4.rotationX(
|
||||
data.y / gravity * pi / 2,
|
||||
)..multiply(
|
||||
Matrix4.rotationY(
|
||||
data.x / gravity * pi / 2,
|
||||
),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: const FlutterLogo(size: 72),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
179
add_to_app/plugin/flutter_module_using_plugin/lib/main.dart
Normal file
179
add_to_app/plugin/flutter_module_using_plugin/lib/main.dart
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright 2019 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:url_launcher/url_launcher.dart' as launcher;
|
||||
|
||||
import 'cell.dart';
|
||||
|
||||
/// The entrypoint for the flutter module.
|
||||
void main() {
|
||||
// This call ensures the Flutter binding has been set up before creating the
|
||||
// MethodChannel-based model.
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
final model = CounterModel();
|
||||
|
||||
runApp(ChangeNotifierProvider.value(value: model, child: const MyApp()));
|
||||
}
|
||||
|
||||
/// This is on alternate entrypoint for this module to display Flutter UI in
|
||||
/// a (multi-)view integration scenario.
|
||||
// This is unfortunately in this file due to
|
||||
// https://github.com/flutter/flutter/issues/72630.
|
||||
void showCell() {
|
||||
runApp(const Cell());
|
||||
}
|
||||
|
||||
/// A simple model that uses a [MethodChannel] as the source of truth for the
|
||||
/// state of a counter.
|
||||
///
|
||||
/// Rather than storing app state data within the Flutter module itself (where
|
||||
/// the native portions of the app can't access it), this module passes messages
|
||||
/// back to the containing app whenever it needs to increment or retrieve the
|
||||
/// value of the counter.
|
||||
class CounterModel extends ChangeNotifier {
|
||||
CounterModel() {
|
||||
_channel.setMethodCallHandler(_handleMessage);
|
||||
_channel.invokeMethod<void>('requestCounter');
|
||||
}
|
||||
|
||||
final _channel = const MethodChannel('dev.flutter.example/counter');
|
||||
|
||||
int _count = 0;
|
||||
|
||||
int get count => _count;
|
||||
|
||||
void increment() {
|
||||
_channel.invokeMethod<void>('incrementCounter');
|
||||
}
|
||||
|
||||
Future<dynamic> _handleMessage(MethodCall call) async {
|
||||
if (call.method == 'reportCounter') {
|
||||
_count = call.arguments as int;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The "app" displayed by this module.
|
||||
///
|
||||
/// It offers two routes, one suitable for displaying as a full screen and
|
||||
/// another designed to be part of a larger UI.
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Module Title',
|
||||
theme: ThemeData.light(),
|
||||
routes: {
|
||||
'/': (context) => const FullScreenView(),
|
||||
'/mini': (context) => const Contents(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps [Contents] in a Material [Scaffold] so it looks correct when displayed
|
||||
/// full-screen.
|
||||
class FullScreenView extends StatelessWidget {
|
||||
const FullScreenView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Full-screen Flutter with plugin')),
|
||||
body: const Contents(showExit: true),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The actual content displayed by the module.
|
||||
///
|
||||
/// This widget displays info about the state of a counter and how much room (in
|
||||
/// logical pixels) it's been given. It also offers buttons to increment the
|
||||
/// counter, opening the Flutter documentation via the url_launcher plugin, and
|
||||
/// (optionally) close the Flutter view.
|
||||
class Contents extends StatelessWidget {
|
||||
final bool showExit;
|
||||
|
||||
const Contents({super.key, this.showExit = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final mediaInfo = MediaQuery.of(context);
|
||||
|
||||
return SizedBox.expand(
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Positioned.fill(
|
||||
child: Opacity(
|
||||
opacity: .25,
|
||||
child: FittedBox(fit: BoxFit.cover, child: FlutterLogo()),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Window is ${mediaInfo.size.width.toStringAsFixed(1)} x '
|
||||
'${mediaInfo.size.height.toStringAsFixed(1)}',
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Consumer<CounterModel>(
|
||||
builder: (context, model, child) {
|
||||
return Text(
|
||||
'Taps: ${model.count}',
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Consumer<CounterModel>(
|
||||
builder: (context, model, child) {
|
||||
return ElevatedButton(
|
||||
onPressed: () => model.increment(),
|
||||
child: const Text('Tap me!'),
|
||||
);
|
||||
},
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
// Use the url_launcher plugin to open the Flutter docs in
|
||||
// a browser.
|
||||
final url = Uri.parse('https://flutter.dev/docs');
|
||||
if (await launcher.canLaunchUrl(url)) {
|
||||
await launcher.launchUrl(url);
|
||||
}
|
||||
},
|
||||
child: const Text('Open Flutter Docs'),
|
||||
),
|
||||
if (showExit) ...[
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
onPressed: () => SystemNavigator.pop(),
|
||||
child: const Text('Exit this screen'),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user