Flutter - 'enableInteractiveSelection' not working for EditableText - android

I am trying to use Flutter's 'EditableText' class.
I have 3 questions regarding it.
'enableInteractiveSelection' is set to be 'true' but I do not see 'cut, copy, paste'.
'onSelectionChanged' seems not working. I don't see anything in the console even when I change selection.
can someone explain me how to use 'selectionControls'?
Below is my 'EditableText' code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Editable Text'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController _mainContentController;
TextSelectionControls _mainContentSelectionController;
FocusNode _textFieldFocusNode;
#override
void initState() {
_textFieldFocusNode = FocusNode();
_mainContentController = TextEditingController();
super.initState();
}
#override
void dispose() {
_mainContentController.dispose();
_textFieldFocusNode.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: <Widget>[
Text('Editable Text Below'),
Container(
padding: EdgeInsets.all(10),
child: EditableText(
focusNode: _textFieldFocusNode,
controller: _mainContentController,
selectionColor: Colors.blue,
style: TextStyle(color: Colors.black, fontSize: 17),
cursorColor: Colors.blue,
textInputAction: TextInputAction.newline,
maxLines: null,
onChanged: (text) {
print(text);
},
enableInteractiveSelection: true,
onSelectionChanged:
(TextSelection textSelection, SelectionChangedCause cause) {
print('working?');
print(textSelection.start);
print(cause);
},
selectionControls: _mainContentSelectionController,
),
)
],
),
);
}
}

You can just use the TextField which has a custom implementation for interactive controllers for Android and iOS.
This is an implementation for Android :
class _MaterialTextSelectionControls extends TextSelectionControls {
#override
Size handleSize = const Size(_kHandleSize, _kHandleSize);
/// Builder for material-style copy/paste text selection toolbar.
#override
Widget buildToolbar(BuildContext context, Rect globalEditableRegion, Offset position, TextSelectionDelegate delegate) {
assert(debugCheckHasMediaQuery(context));
assert(debugCheckHasMaterialLocalizations(context));
return ConstrainedBox(
constraints: BoxConstraints.tight(globalEditableRegion.size),
child: CustomSingleChildLayout(
delegate: _TextSelectionToolbarLayout(
MediaQuery.of(context).size,
globalEditableRegion,
position,
),
child: _TextSelectionToolbar(
handleCut: canCut(delegate) ? () => handleCut(delegate) : null,
handleCopy: canCopy(delegate) ? () => handleCopy(delegate) : null,
handlePaste: canPaste(delegate) ? () => handlePaste(delegate) : null,
handleSelectAll: canSelectAll(delegate) ? () => handleSelectAll(delegate) : null,
),
)
);
}
/// Builder for material-style text selection handles.
#override
Widget buildHandle(BuildContext context, TextSelectionHandleType type, double textHeight) {
final Widget handle = Padding(
padding: const EdgeInsets.only(right: 26.0, bottom: 26.0),
child: SizedBox(
width: _kHandleSize,
height: _kHandleSize,
child: CustomPaint(
painter: _TextSelectionHandlePainter(
color: Theme.of(context).textSelectionHandleColor
),
),
),
);
// [handle] is a circle, with a rectangle in the top left quadrant of that
// circle (an onion pointing to 10:30). We rotate [handle] to point
// straight up or up-right depending on the handle type.
switch (type) {
case TextSelectionHandleType.left: // points up-right
return Transform(
transform: Matrix4.rotationZ(math.pi / 2.0),
child: handle
);
case TextSelectionHandleType.right: // points up-left
return handle;
case TextSelectionHandleType.collapsed: // points up
return Transform(
transform: Matrix4.rotationZ(math.pi / 4.0),
child: handle
);
}
assert(type != null);
return null;
}
}
You can check the source code from text_selection.dart, the same for cupertino.
In your sample you are using a null variable _mainContentSelectionController for that reason you don't see anything when long press your input.
Try creating a new class from TextSelectionControls and copy the logic from _MaterialTextSelectionControls.
Sample:
selectionControls: MySelectionControls(),
...
class MySelectionControls extends TextSelectionControls {
#override
Widget buildHandle(BuildContext context, TextSelectionHandleType type, double textLineHeight) {
// TODO: implement buildHandle
return null;
}
#override
Widget buildToolbar(BuildContext context, Rect globalEditableRegion, Offset position, TextSelectionDelegate delegate) {
// TODO: implement buildToolbar
return null;
}
#override
// TODO: implement handleSize
Size get handleSize => null;
}

Related

Flutter ReorderableDragStartListener issue detecting gesture on mobile device (but works fine in web browser)

I want to have reorderable list in flutter with custom drag handle (that works immediately, without long press first).
To achieve that I did:
buildDefaultDragHandles: false,
and I used ReorderableDragStartListener.
code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final List<int> _items = List<int>.generate(50, (int index) => index);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: ReorderableListView(
buildDefaultDragHandles: false,
children: <Widget>[
for (int index = 0; index < _items.length; index++)
Container(
key: Key('$index'),
color: _items[index].isOdd ? Colors.blue[100] : Colors.red[100],
child: Row(
children: <Widget>[
Container(
width: 64,
height: 64,
padding: const EdgeInsets.all(8),
child: ReorderableDragStartListener(
index: index,
child: Card(
color: Colors.green,
elevation: 2,
),
),
),
Text('Item ${_items[index]}'),
],
),
),
],
onReorder: (int oldIndex, int newIndex) {
print('oldIndex $oldIndex, newIndex $newIndex');
},
),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
On desktop (e.g. when run in Edge) it works as expected, drag handle is clicked (mouse down) and dragged up or down to change order.
The problem is on mobile device. When I tap down, and I move finger up or down - the scroll is performed. When however I tap down, and move finger little left or right, and then up/down -> then reordering happens. (tested in android emulator and real android device).
Question is - why on mobile I need to do this little annoying additional left/right move before chaining order? How to fix it?
How it works on desktop (Edge):
How it work on Android (bug!):
I solved it using custom ReorderableDragStartListener, when I set tap delay to 1ms. Since this approach does not require moving finger left/right before dragging, and 1ms is low time, it works like a charm.
code:
import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart';
class CustomReorderableDelayedDragStartListener extends ReorderableDragStartListener {
final Duration delay;
const CustomReorderableDelayedDragStartListener({
this.delay = kLongPressTimeout,
Key? key,
required Widget child,
required int index,
bool enabled = true,
}) : super(key: key, child: child, index: index, enabled: enabled);
#override
MultiDragGestureRecognizer createRecognizer() {
return DelayedMultiDragGestureRecognizer(delay: delay, debugOwner: this);
}
}
usage:
CustomReorderableDelayedDragStartListener(
delay: const Duration(milliseconds: 1), // or any other duration that fits you
index: widget.index, // passed from parent
child: Container(
child: const Icon( // or any other graphical element
Icons.drag_handle
),
),
)
Try this
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatefulWidget(),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
final List<int> _items = List<int>.generate(20, (int index) => index);
#override
Widget build(BuildContext context) {
final ColorScheme colorScheme = Theme.of(context).colorScheme;
final Color oddItemColor = colorScheme.primary.withOpacity(0.05);
final Color evenItemColor = colorScheme.primary.withOpacity(0.15);
return ReorderableListView(
buildDefaultDragHandles: false,
children: <Widget>[
for (int index = 0; index < _items.length; index++)
Container(
key: Key('$index'),
color: _items[index].isOdd ? oddItemColor : evenItemColor,
child: Row(
children: <Widget>[
Container(
width: 64,
height: 64,
padding: const EdgeInsets.all(8),
child: ReorderableDragStartListener(
index: index,
child: Card(
color: colorScheme.primary,
elevation: 2,
),
),
),
Text('Item ${_items[index]}'),
],
),
),
],
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final int item = _items.removeAt(oldIndex);
_items.insert(newIndex, item);
});
},
);
}
}

flutter checkbox not working in StatelessWidget

Here is my class
class Home extends StatelessWidget {
and the Checkbox goes here.
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
TextField(
controller: ctrlMotherName,
decoration: InputDecoration(
labelText: "Name of Mother",
border: OutlineInputBorder()
)
),
SizedBox(height: 10,),
Checkbox(
value: false,
onChanged: (bool val){
},
),
I can't able to check the checkbox. Same issue found when I use Radiobutton also.
You need to use a StatefulWidget since you're dealing with changing values. I've provided an example:
class MyAppOne extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyAppOne> {
bool _myBoolean = false;
#override
Widget build(BuildContext context) {
return Center(
child: Checkbox(
value: _myBoolean,
onChanged: (value) {
setState(() {
_myBoolean = value; // rebuilds with new value
});
},
),
);
}
}
One way you can achieve this is using the provider package. I tried to create the shortest possible app to show how you can use it. The neat part is that you get to keep your widget stateless.
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:provider/provider.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Home(),
);
}
}
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ChangeNotifierProvider(
create: (_) => CheckboxProvider(),
child: Consumer<CheckboxProvider>(
builder: (context, checkboxProvider, _) => Checkbox(
value: checkboxProvider.isChecked,
onChanged: (value) {
checkboxProvider.isChecked = value ?? true;
},
),
),
),
),
);
}
}
class CheckboxProvider with ChangeNotifier {
bool _isChecked = false;
bool get isChecked => _isChecked;
set isChecked(bool value) {
_isChecked = value;
notifyListeners();
}
}
It took me quite some time to understand the package but it is very useful and recommended if you want an easier way to manage state in your application. Here's a video from the Flutter team explaining how to use Provider. I would still recommend spending some time looking further into it.
P.S.: Don't forget to change the pubspec.yaml file.
think of flutter like javascript and pass the position as a parameter to the medCheckedChanged function in the list builder. when the dart parser evaluates the expression or lambda function it will invoke the method with the position parameter as a value.
class testWidget2 extends StatefulWidget {
testWidget2({Key key}) : super(key: key);
int numberLines = 50;
List<bool> checkBoxValues = [];
#override
_testWidget2State createState() => _testWidget2State();
}
class _testWidget2State extends State<testWidget2> {
_medCheckedChanged(bool value, int position) {
setState(() => widget.checkBoxValues[position] = value);
}
#override
void initState() {
// TODO: implement initState
super.initState();
int i = 0;
setState(() {
while (i < widget.numberLines) {
widget.checkBoxValues.add(false);
i++;
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: ListView.builder(
itemCount: widget.checkBoxValues.length,
itemBuilder: (context, position) {
return Container(
height: MediaQuery.of(context).size.width * .06,
width: MediaQuery.of(context).size.height * .14,
alignment: Alignment(0, 0),
child: Checkbox(
activeColor: Color(0xff06bbfb),
value: widget.checkBoxValues[position],
onChanged: (newValue) {
_medCheckedChanged(newValue, position);
}, //pass to medCheckedChanged() the position
),
);
})));
}
}
You can use StateProvider from the Riverpod package to achieve this.
final checkboxProvider = StateProvider<bool>((ref) => false);
class CheckboxWidget extends StatelessWidget {
const CheckboxWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Row(
children: [
Consumer(
builder: (context, ref, _) {
return Checkbox(
value: ref.watch(checkboxProvider),
onChanged: (value) {
ref.read(checkboxProvider.state).state = value!;
},
);
}
),
Text('On'),
],
);
}
}

Bottom Naviagtion Bar not hidden after pushed to new screen

I have two screen A and B, screen A has a bottom navigation bar.
After i pushed screen A to screen B, bottom navigation bar of screen A still pin on screen B.
I want show full screen B without bottom navigation of screen A.
This is a screen A, it has a bottom naviagtion bar:
class Parent extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'TechOne',
theme: ThemeData(
primarySwatch: Colors.red,
),
home: MyParentPage(title: 'TechOne'),
);
}
}
/*StatefulWidget is Widget with mutable*/
class MyParentPage extends StatefulWidget {
MyParentPage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyParentPageState createState() => _MyParentPageState();
}
/*State is a manager of StatefulWidget*/
class _MyParentPageState extends State<MyParentPage>
with SingleTickerProviderStateMixin {
var _itemSelected = 0;
TabController _tabController;
final _bodyUI = [
HomeUI(),
SearchUI(),
Center(
child: Text('Notification'),
),
Center(
child: Text('Account'),
),
];
_onBottomNavigationBarTap(int index) {
print(_itemSelected);
setState(() {
_itemSelected = index;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
_tabController = TabController(length: 4, vsync: this);
}
#override
Widget build(BuildContext context) {
_tabController.animateTo(_itemSelected);
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: Values.itemsBottomNavigationBar,
onTap: (index) {
_onBottomNavigationBarTap(index);
},
currentIndex: _itemSelected,
selectedItemColor: Colors.red,
backgroundColor: Colors.white,
type: BottomNavigationBarType.fixed,
),
body: TabBarView(
physics: NeverScrollableScrollPhysics(),
controller: _tabController,
children: <Widget>[
_bodyUI[0],
_bodyUI[0],
_bodyUI[2],
_bodyUI[3],
]));
}
}
Inside _bodyUI[0] widget, I push to screen B:
Navigator.push(context, MaterialPageRoute(builder: (context) => SearchUI()));
This is a screen B, bottom navigation bar still pin on here, i want hidden it:
class SearchUI extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
title: 'Search',
theme: ThemeData(primarySwatch: Colors.red),
home: MySearchPage(),
);
}
}
class MySearchPage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _MySearchState();
}
}
class _MySearchState extends State<MySearchPage> {
final TextEditingController _textEditingController = TextEditingController();
final FocusNode _focusNode = FocusNode();
TextField _appBarTitle;
#override
void initState() {
// TODO: implement initState
super.initState();
_appBarTitle = TextField(
controller: _textEditingController,
focusNode: _focusNode,
autofocus: true,
textInputAction: TextInputAction.done,
textCapitalization: TextCapitalization.sentences,
cursorColor: Colors.white,
cursorRadius: Radius.circular(16),
maxLines: 1,
style: TextStyle(
color: Colors.white,
),
decoration: InputDecoration(
border: InputBorder.none,
prefixIcon: Icon(
Icons.search,
color: Colors.white,
),
suffixIcon: IconButton(icon: Icon(Icons.clear, color: Colors.white,), onPressed: (){
_textEditingController.clear();
}),
hintText: 'Search...',
hintStyle:
TextStyle(color: Colors.white.withOpacity(0.5), fontSize: 18)));
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: _appBarTitle,
),
body: Center(
child: Text('Search Screen'),
),
);
}
}
Your code is calling a SearchUI() class as one of the TabBarViews :
final _bodyUI = [
HomeUI(),
SearchUI(),
Center(
child: Text('Notification'),
),
Center(
child: Text('Account'),
),
];
Which means it's going to only change the view and keep the bar there.
EDIT : in a comment I deleted earlier I mentioned the nested MaterialApps may cause an issue. That seems to help correct the problem but in the comments below you now mention the addition of a back arrow. The quote below is taken from the Flutter documentation for AppBar.
If the leading widget is omitted, but the AppBar is in a Scaffold with
a Drawer, then a button will be inserted to open the drawer.
Otherwise, if the nearest Navigator has any previous routes, a
BackButton is inserted instead. This behavior can be turned off by
setting the automaticallyImplyLeading to false. In that case a null
leading widget will result in the middle/title widget stretching to
start.
Basically, that is something you can turn off using the property mentioned above.

Dynamically pass stateless widgets as child on button click -Flutter

I have been trying to create backdrop menu with some navigation buttons each with his own stateless widget page . I want to pass the child widget page based on index position of the clicked menu.
backlayer.dart
class BackLayer extends StatefulWidget {
AnimationController controller;
Widget child;
BackLayer({this.controller});
_BackLayerState createState() => _BackLayerState();
}
class _BackLayerState extends State<BackLayer> {
var _currentPageIndex;
List<String> navlist = ['Home', 'About', 'Contact Us'];
bool get isPanelVisible {
final AnimationStatus status = widget.controller.status;
return status == AnimationStatus.completed ||
status == AnimationStatus.forward;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: navlist.length,
itemBuilder: (BuildContext context, int index) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
ListTile(
onTap: () {
setState(() {
_currentPageIndex = navlist[index];
});
// PanelClass(frontLayer: About(),);
print(navlist[index].toString());
widget.controller
.fling(velocity: isPanelVisible ? -1.0 : 1.0);
},
title: Text(
navlist[index],
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.black87, fontWeight: FontWeight.bold),
),
)
],
);
I want to pass the reference of the clicked menu inside backdrop.dart ,where i am gonna pass that reference to panelClass body as the child .
Backdrop.dart
class BackDrop extends StatefulWidget {
_BackDropState createState() => _BackDropState();
}
class _BackDropState extends State<BackDrop> with TickerProviderStateMixin {
AnimationController controller;
#override
void initState() {
super.initState();
controller = AnimationController(
duration: Duration(seconds: 3), vsync: this, value: 1.0);
}
#override
void dispose() {
super.dispose();
controller.dispose();
}
bool get isPanelVisible {
final AnimationStatus status = controller.status;
return status == AnimationStatus.completed ||
status == AnimationStatus.forward;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0.0,
backgroundColor: Theme.of(context).accentColor,
title: Text('Home'),
leading: IconButton(
onPressed: () {
controller.fling(velocity: isPanelVisible ? -1.0 : 1.0);
},
icon: AnimatedIcon(
progress: controller.view,
icon: AnimatedIcons.close_menu,
),
),
),
body: PanelClass(controller: controller,frontLayer: 'Need to get reference here',),
);
}
}

make clickable below page in stack flutter

I have just a stack that contains a guillotine menu and a list. But below page (list and fab) isn't clickable! First code is Main page, second code is books (below page) and third code is guillotine menu.
How can I make clickable below page?
main page
import 'package:firebase_example/Ui/Books.dart';
import 'package:firebase_example/Ui/GuillotineMenu.dart';
import 'package:flutter/material.dart';
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final GlobalKey<ScaffoldState> _drawerKey = new GlobalKey();
#override
Widget build(BuildContext context) {
return new MaterialApp(
debugShowCheckedModeBanner: false,
home: SafeArea(
top: false,
bottom: false,
child: new Container(
child: new Stack(
alignment: Alignment.topLeft,
children: <Widget>[
Books(),
GuillotineMenu(),
],
),
),
),
);
What you're looking for is IgnorePointer. By using it you should be able to exclude your widget from hit testing (and so allow things under it to be touched instead).
You're going to have to be careful about how you implement the button that makes the guillotine menu open/close though. I'd advise making it a part of the item lower in the stack, and then setting IgnorePointer's ignoring = true when the guillotine is closed.
Note that there might be a 'better' way of implementing the guillotine menu using PageRoutes. That way you'd just be pushing/popping a new route on top of the existing navigator rather than having to maintain your own stack.
Here's the code:
class GuillotinePageRoute<T> extends PageRoute<T> {
GuillotinePageRoute({
#required this.builder,
RouteSettings settings: const RouteSettings(),
this.maintainState: true,
bool fullscreenDialog: false,
}) : assert(builder != null),
super(settings: settings, fullscreenDialog: fullscreenDialog);
final WidgetBuilder builder;
#override
final bool maintainState;
#override
Duration get transitionDuration => const Duration(milliseconds: 500);
#override
Color get barrierColor => null;
#override
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
final Widget result = builder(context);
assert(() {
if (result == null) {
throw new FlutterError('The builder for route "${settings.name}" returned null.\n'
'Route builders must never be null.');
}
return true;
}());
return result;
}
#override
Widget buildTransitions(
BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
MediaQueryData queryData = MediaQuery.of(context);
var topInset = queryData.padding.top;
Offset origin = Offset((kToolbarHeight / 2.0), topInset + (kToolbarHeight / 2.0));
Curve curve = animation.status == AnimationStatus.forward ? Curves.bounceOut : Curves.bounceIn;
var rotateTween = new Tween(begin: -pi / 2.0, end: 0.0);
Cubic opacityCurve = Cubic(0.0, 1.0, 0.0, 1.0);
return new AnimatedBuilder(
animation: animation,
child: child,
builder: (context, child) {
return Opacity(
opacity: opacityCurve.transform(animation.value),
child: Transform(
transform: Matrix4.identity()..rotateZ(rotateTween.lerp(curve.transform(animation.value))),
origin: origin,
child: child,
),
);
},
);
}
#override
String get barrierLabel => null;
}
And using it in an example:
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MenuPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(children: [
AppBar(
leading: new IconButton(icon: Icon(Icons.menu), onPressed: () => Navigator.pop(context)),
elevation: 0.0,
),
Expanded(
child: Container(
child: Center(
child: Text(
"Menu page!",
style: TextStyle(color: Colors.white, decoration: TextDecoration.none),
),
),
color: Colors.blue,
),
),
]);
}
}
class MyHomePage extends StatelessWidget {
void pushGuillotine(BuildContext context, WidgetBuilder builder) {
Navigator.push(context, new GuillotinePageRoute(builder: builder));
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("This is a title"),
leading: new RotatedBox(
quarterTurns: -1,
child: IconButton(icon: Icon(Icons.menu), onPressed: () => pushGuillotine(context, (context) => MenuPage())),
),
),
body: Container(
color: Colors.blueGrey,
child: Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'This is the home page.',
),
new Text(
'Hello world!',
style: Theme.of(context).textTheme.display1,
),
],
),
),
),
);
}
}
class GuillotinePageRoute<T> extends PageRoute<T> {
GuillotinePageRoute({
#required this.builder,
RouteSettings settings: const RouteSettings(),
this.maintainState: true,
bool fullscreenDialog: false,
}) : assert(builder != null),
super(settings: settings, fullscreenDialog: fullscreenDialog);
final WidgetBuilder builder;
#override
final bool maintainState;
#override
Duration get transitionDuration => const Duration(milliseconds: 500);
#override
Color get barrierColor => null;
#override
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
final Widget result = builder(context);
assert(() {
if (result == null) {
throw new FlutterError('The builder for route "${settings.name}" returned null.\n'
'Route builders must never be null.');
}
return true;
}());
return result;
}
#override
Widget buildTransitions(
BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
MediaQueryData queryData = MediaQuery.of(context);
var topInset = queryData.padding.top;
Offset origin = Offset((kToolbarHeight / 2.0), topInset + (kToolbarHeight / 2.0));
Curve curve = animation.status == AnimationStatus.forward ? Curves.bounceOut : Curves.bounceIn;
var rotateTween = new Tween(begin: -pi / 2.0, end: 0.0);
Cubic opacityCurve = Cubic(0.0, 1.0, 0.0, 1.0);
return new AnimatedBuilder(
animation: animation,
child: child,
builder: (context, child) {
return Opacity(
opacity: opacityCurve.transform(animation.value),
child: Transform(
transform: Matrix4.identity()..rotateZ(rotateTween.lerp(curve.transform(animation.value))),
origin: origin,
child: child,
),
);
},
);
}
#override
String get barrierLabel => null;
}
It's a stack use the gesture detector's onTapDown which is not available in a floating action button
If you have a widget with no interaction and want to make it clickable use the GestureDetector widget as parent, you can see the example in the link.
--- edit
This answer helps you when you need to make a widget without interaction have some, not exactly what this question is talk about, honest mistake but i could help others..

Categories

Resources