Skip to content

Commit

Permalink
Modify Floating Action Button
Browse files Browse the repository at this point in the history
  • Loading branch information
yungwenpeng committed Nov 14, 2022
1 parent 4c6d688 commit 809c146
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 3 deletions.
3 changes: 3 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.thingsboard_app">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:label="thingsboard_app"
android:name="${applicationName}"
Expand Down
Binary file modified flutter_with_localapi_example.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import 'package:thingsboard_app/pages/users.dart';
import 'package:url_strategy/url_strategy.dart';

import 'models/models.dart';
Expand Down
238 changes: 238 additions & 0 deletions lib/pages/expandable_fab.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class ExpandableFab extends StatefulWidget {
const ExpandableFab({
super.key,
this.initialOpen,
required this.distance,
required this.children,
});

final bool? initialOpen;
final double distance;
final List<Widget> children;

@override
State<ExpandableFab> createState() => _ExpandableFabState();
}

class _ExpandableFabState extends State<ExpandableFab>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
late final Animation<double> _expandAnimation;
bool _open = false;

@override
void initState() {
super.initState();
_open = widget.initialOpen ?? false;
_controller = AnimationController(
value: _open ? 1.0 : 0.0,
duration: const Duration(milliseconds: 250),
vsync: this,
);
_expandAnimation = CurvedAnimation(
curve: Curves.fastOutSlowIn,
reverseCurve: Curves.easeOutQuad,
parent: _controller,
);
}

@override
void dispose() {
_controller.dispose();
super.dispose();
}

void _toggle() {
setState(() {
_open = !_open;
if (_open) {
_controller.forward();
} else {
_controller.reverse();
}
});
}

@override
Widget build(BuildContext context) {
return SizedBox.expand(
child: Stack(
alignment: Alignment.bottomCenter,
clipBehavior: Clip.none,
children: [
_buildTapToCloseFab(),
..._buildExpandingActionButtons(),
_buildTapToOpenFab(),
],
),
);
}

Widget _buildTapToCloseFab() {
return SizedBox(
width: 56.0,
height: 56.0,
child: Center(
child: Material(
shape: const CircleBorder(),
clipBehavior: Clip.antiAlias,
elevation: 4.0,
child: InkWell(
onTap: _toggle,
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(
Icons.close,
color: Colors.orangeAccent,
),
),
),
),
),
);
}

List<Widget> _buildExpandingActionButtons() {
final children = <Widget>[];
final count = widget.children.length;
final step = 120.0 / (count - 1);
for (var i = 0, angleInDegrees = 30.0;
i < count;
i++, angleInDegrees += step) {
children.add(
_ExpandingActionButton(
directionInDegrees: angleInDegrees,
maxDistance: widget.distance,
progress: _expandAnimation,
child: widget.children[i],
),
);
}
return children;
}

Widget _buildTapToOpenFab() {
return IgnorePointer(
ignoring: _open,
child: AnimatedContainer(
transformAlignment: Alignment.center,
transform: Matrix4.diagonal3Values(
_open ? 0.7 : 1.0,
_open ? 0.7 : 1.0,
1.0,
),
duration: const Duration(milliseconds: 250),
curve: const Interval(0.0, 0.5, curve: Curves.easeOut),
child: AnimatedOpacity(
opacity: _open ? 0.0 : 1.0,
curve: const Interval(0.25, 1.0, curve: Curves.easeInOut),
duration: const Duration(milliseconds: 250),
child: FloatingActionButton(
onPressed: _toggle,
heroTag: "viewModule",
tooltip: AppLocalizations.of(context)!.usersFloatActionViewModule,
elevation: 0.0,
backgroundColor: const Color.fromARGB(255, 102, 168, 223),
hoverColor: Colors.orange,
child: const Icon(Icons.view_module),
),
),
),
);
}
}

@immutable
class _ExpandingActionButton extends StatelessWidget {
const _ExpandingActionButton({
required this.directionInDegrees,
required this.maxDistance,
required this.progress,
required this.child,
});

final double directionInDegrees;
final double maxDistance;
final Animation<double> progress;
final Widget child;

@override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
return AnimatedBuilder(
animation: progress,
builder: (context, child) {
final offset = Offset.fromDirection(
directionInDegrees * (math.pi / 180.0),
progress.value * maxDistance,
);
return Positioned(
right: (width / 2) - 20.0 + offset.dx,
bottom: 14.0 + offset.dy,
child: Transform.rotate(
angle: (1.0 - progress.value) * math.pi / 2,
child: child!,
),
);
},
child: FadeTransition(
opacity: progress,
child: child,
),
);
}
}

@immutable
class ActionButton extends StatelessWidget {
const ActionButton({
super.key,
this.onPressed,
required this.icon,
});

final VoidCallback? onPressed;
final Widget icon;

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Material(
shape: const CircleBorder(),
clipBehavior: Clip.antiAlias,
color: theme.colorScheme.secondary,
elevation: 4.0,
child: IconButton(
onPressed: onPressed,
icon: icon,
color: theme.colorScheme.onSecondary,
),
);
}
}

@immutable
class FakeItem extends StatelessWidget {
const FakeItem({
super.key,
required this.isBig,
});

final bool isBig;

@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 24.0),
height: isBig ? 128.0 : 36.0,
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(8.0)),
color: Colors.grey.shade300,
),
);
}
}
1 change: 1 addition & 0 deletions lib/pages/pages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export 'home.dart';
export 'login.dart';
export 'drawer.dart';
export 'users.dart';
export 'expandable_fab.dart';
29 changes: 27 additions & 2 deletions lib/pages/users.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,31 @@ class _UserListState extends State<UserList> {
body:
columnCount == 2 ? createGridBody(context) : createListBody(context),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: Row(
floatingActionButton: ExpandableFab(
distance: 80.0,
children: [
ActionButton(
onPressed: (() {
changeMode();
}),
icon: columnCount == 2
? const Icon(Icons.view_list)
: const Icon(Icons.grid_view),
),
ActionButton(
onPressed: (() {}),
icon: const Icon(Icons.table_rows),
),
ActionButton(
onPressed: (() {
_showEditUserDialog(
context, 'ADD', "", "", "", _users.length - 1);
}),
icon: const Icon(Icons.add),
),
],
)
/*Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FloatingActionButton(
Expand Down Expand Up @@ -128,7 +152,8 @@ class _UserListState extends State<UserList> {
child: const Icon(Icons.add))
: const SizedBox(),
],
),
)*/
,
);
}

Expand Down

0 comments on commit 809c146

Please sign in to comment.