floating_windows 1.0.0 floating_windows: ^1.0.0 copied to clipboard
A widget wrapper that allows a floating widget be dragged and rescaled.
import 'package:flutter/material.dart';
import 'package:floating_windows/floating_windows.dart';
import 'package:provider/provider.dart';
import 'dart:math' as math;
void main() {
runApp(const App());
}
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// Create one of this and pass it to the FloatingOverlay, to be able to pop
// and push new pages and the floating overlay don't continue showing in
// all new pages on top and show again when you come back
final routeObserver = RouteObserver();
return MaterialApp(
title: 'Floating Windows Example',
navigatorObservers: [routeObserver], // Give it to the main materialApp
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Provider<RouteObserver>(
// One way to make it avaliable through all your files and pages, but
// global variables and other means will work just fine as well.
create: (_) => routeObserver,
child: const HomePage(),
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final controller1 = FloatingOverlayController.absoluteSize(
maxSize: const Size(800, 800),
minSize: const Size(400, 300),
start: Offset.zero,
padding: const EdgeInsets.all(20.0),
constrained: true,
);
final controller2 = FloatingOverlayController.absoluteSize(
maxSize: const Size(800, 800),
minSize: const Size(400, 300),
start: const Offset(100, 100),
padding: const EdgeInsets.all(20.0),
constrained: true,
);
final controller3 = FloatingOverlayController.absoluteSize(
maxSize: const Size(800, 800),
minSize: const Size(300, 500),
start: const Offset(300, 300),
padding: const EdgeInsets.all(20.0),
constrained: true,
);
final routeObserver = Provider.of<RouteObserver>(context, listen: false);
return Scaffold(
appBar: AppBar(
title: const Text('Floating Windows Example'),
centerTitle: true,
),
body: FloatingOverlay(
// Passing the RouteObserver created at line 17 as a parameter, will
// make so that when you push pages on top of this one, the floating
// child will vanish and reappear when you return.
routeObserver: routeObserver,
controllers: [controller1, controller2, controller3],
floatingChildren: [
SizedBox(
width: 400,
height: 300,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
border: Border.all(
color: Colors.black,
width: 5.0,
),
),
),
),
SizedBox(
width: 400,
height: 300,
child: Container(
decoration: BoxDecoration(
color: Colors.amber,
border: Border.all(
color: Colors.black,
width: 5.0,
),
),
),
),
SizedBox(
width: 300,
height: 500,
child: Container(
decoration: BoxDecoration(
color: Color((math.Random().nextDouble() * 0xFFFFFF).toInt()).withOpacity(1.0),
border: Border.all(
color: Colors.black,
width: 5.0,
),
),
),
),
],
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CustomButton(
title: 'Toggle Overlay 1',
onPressed: () {
controller1.toggle();
},
),
CustomButton(
title: 'Toggle Overlay 2',
onPressed: () {
controller2.toggle();
},
),
CustomButton(
title: 'Toggle Overlay 3',
onPressed: () {
controller3.toggle();
},
),
CustomButton(
title: 'Set Screen Center Offset',
onPressed: () {
final size = MediaQuery.of(context).size;
final rect = Rect.fromPoints(
Offset.zero,
Offset(size.width, size.height),
);
controller1.offset = rect.center;
},
),
CustomButton(
title: 'Set Scale to 2.0',
onPressed: () {
controller1.scale = 2.0;
},
),
CustomButton(
title: 'New Page',
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const NewPage()),
);
},
),
CustomButton(
title: 'New Page with AnimationController',
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => Provider<RouteObserver>(
// There is a difference to initializing the
// controller inside a Stateful Widget that has an
// [AnimationController].
create: (_) => routeObserver,
child: const AnimationPage(),
),
),
);
},
),
],
),
),
),
);
}
}
class CustomButton extends StatelessWidget {
const CustomButton({
Key? key,
required this.onPressed,
required this.title,
}) : super(key: key);
final VoidCallback onPressed;
final String title;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
child: Text(title),
onPressed: onPressed,
),
);
}
}
class NewPage extends StatelessWidget {
const NewPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('New Page'), centerTitle: true),
);
}
}
class AnimationPage extends StatefulWidget {
const AnimationPage({Key? key}) : super(key: key);
@override
_AnimationPageState createState() => _AnimationPageState();
}
class _AnimationPageState extends State<AnimationPage> with SingleTickerProviderStateMixin {
late final AnimationController animationController;
late final FloatingOverlayController controller;
@override
void initState() {
animationController = AnimationController(
vsync: this,
duration: const Duration(seconds: 1),
);
/// The [FloatingOverlayController] needs to be initialized only once when
/// there is an [AnimationController] inside the same State.
controller = FloatingOverlayController.absoluteSize(
maxSize: const Size(200, 200),
minSize: const Size(100, 100),
padding: const EdgeInsets.all(20.0),
constrained: true,
);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Floating Overlay Example'),
centerTitle: true,
),
body: FloatingOverlay(
// Passing the RouteObserver created at line 17 as a parameter, will
// make so that when you push pages on top of this one, the floating
// child will vanish and reappear when you return.
routeObserver: Provider.of<RouteObserver>(context, listen: false),
controllers: [controller],
floatingChildren: [
SizedBox.square(
dimension: 100.0,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
border: Border.all(
color: Colors.black,
width: 5.0,
),
),
),
),
],
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
child: const Text('Toggle Overlay'),
onPressed: () {
controller.toggle();
},
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
child: const Text('New Page'),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const NewPage()),
);
},
),
),
],
),
),
),
);
}
}