I have a flutter app in which I have a stack and within a Container with some elements. I want to give some gesture detection and show an overlay layer when the user clicks on it. I have think of creating another container with the color and shape I want and then make it occupy the same space of the other container but above it.
The problem is that I can't figure out how to make the overlay container to have the same size than the main container.
This is what I have basically:
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: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _layerVisible = false;
#override
Widget build(BuildContext context) {
List<Widget> array = List();
array.add(Stack(
children: <Widget>[
GestureDetector(
onTapDown: (TapDownDetails details) {
setState(() {
_layerVisible = true;
});
},
onTapUp: (TapUpDetails details) {
setState(() {
_layerVisible = false;
});
},
child: Container(
padding: EdgeInsets.all(8),
color: Colors.yellow,
child: Column(
children: <Widget>[
Padding(
child: Text("Title text"),
padding: EdgeInsets.only(bottom: 10),
),
Text(
"Veeery long text. This is a dynamic value so we don't really know how long it's going to be...this means that the parent container is going to grow.")
],
)),
),
Visibility(
visible: _layerVisible,
child: Container(
color: Colors.grey,
child: Text(
"This is the container that I want to fit exactly in the yellow container."),
),
)
],
));
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
width: 300,
child: ListView(
children: array,
),
));
}
}
What I want is to make the last container within the stack to occupy the same space of the other container, this way I can create an overlay layer when I click on it.
I try using Expanded, using Positioned.fill, but none of this solution works for me.
Thanks.
Try using an IntrinsicHeight above your Stack. It'll give an explicit size based off the largest widget in the stack to the Stack itself. Then, make sure you add a fit: StackFit.passthrough, on the Stack. That'll pass down the sizing information from IntrinsicHeight to all of it's children.
Related
I am a Flutter beginner and I am currently trying to implement a login screen which must satisfy to the following requirements:
first the screen is made of a widget containing the username and password text fields, that occupies all the screen
the sign in button is anchored at the bottom of the screen
when the soft keyboard is opened, the first widget is no longer expanded to take all the screen
when the soft keyboard is closed, the screen should look like as the one described in the 1st bullet point
the screen should be scrollable (when the soft keyboard is opened, if all the widgets don't fit in the remaining screen not hidden by the keyboard, I still want to scroll to access all the screen's content)
Here are wireframes that describe what I would like to achieve with Flutter:
state: soft keyboard closed
state: soft keyboard opened
Is this feasible with Flutter? Currently here is what I have attempted:
import 'package:flutter/material.dart';
import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _isSoftKeyboardOpen;
#override
void initState() {
super.initState();
var keyboardVisibilityController = KeyboardVisibilityController();
_isSoftKeyboardOpen = keyboardVisibilityController.isVisible;
// Subscribe
keyboardVisibilityController.onChange.listen((bool visible) {
setState(() {
_isSoftKeyboardOpen = visible;
});
});
}
#override
Widget build(BuildContext context) {
var mAppBar = AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
);
return Scaffold(
appBar: mAppBar,
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: LayoutBuilder(
builder: (context, constraint) {
return SingleChildScrollView(
padding: EdgeInsets.only(left: 16, right: 16),
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraint.maxHeight),
child: LayoutBuilder(
builder: (containerContext, constraint) {
return Container(
height: MediaQuery.of(containerContext).size.height - mAppBar.preferredSize.height - MediaQuery.of(context).padding.top,
color: Colors.green,
child: Column(
children: <Widget> [
Expanded(
flex: _isSoftKeyboardOpen ? 0 : 1,
child: Column(
children: <Widget> [
TextFormField(
decoration: InputDecoration(
labelText: "Username",
),
),
TextFormField(
decoration: InputDecoration(
labelText: "Password",
),
)
],
),
),
ElevatedButton(onPressed: null, child: Text("Sign in")
),
],
),
);
}
),
)
);
},
)
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
As you can see when the soft keyboard opens, the screen scrolls but there is unnecessary space below the button (which is the last element of the screen). Is there a way for me to change the screen height dynamically in my code to achieve what I want? Or is there another way to implement the sign in screen which fulfills my requirements.
You can user the below widget to gain your requirements:
return KeyboardVisibilityBuilder(
builder: (context, child, isKeyboardVisible) {
if (isKeyboardVisible) {
// build layout for visible keyboard
} else {
// build layout for invisible keyboard
}
},
child: child, // this widget goes to the builder's child property. Made for better performance.
);
Hi #Jane you can achieve the desired output using the below code.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: LoginPage(),
);
}
}
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('SIGN IN'),
),
body: LayoutBuilder(builder: (context, constraint) {
return ListView(
shrinkWrap: true,
padding: const EdgeInsets.all(16),
children: [
GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: Container(
color: Colors.white,
height: constraint.maxHeight -
32, // -32 to remove vertical padding
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
children: [
Column(
children: [
TextFormField(
decoration: InputDecoration(
labelText: "Username",
),
),
TextFormField(
decoration: InputDecoration(
labelText: "Password",
),
),
],
),
ElevatedButton(onPressed: null, child: Text("Sign in"))
],
),
),
),
],
);
}));
}
}
Also, try to use plugins only in dire situations when you can't achieve a particular task with flutter available resources.
is there anyone who can help me ?
I am currently on a project where i want to visualize pathfinding-algorithms by using flutter (i want to use it as app later on).
My Problem:
I have a gridPaper and it's perfectly formatted for my needs... but how can i make the single elements in it accessible by clicking on them ?
I want to create a 'wall' between the start- and endnode to make it harder for the pathfinding-algorithm. (if that makes sense)
But at first i need to create a start- end endnode as well.
Here is what i have so far:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final appTitle = 'Path Finder';
final Color gridColor = Colors.lightBlue[100];
#override
Widget build(BuildContext context) {
return MaterialApp(
title: appTitle,
home: MyHomePage(title: appTitle),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
MyHomePage({Key key, this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: GridPaper(
child: Container(),
color: Colors.lightBlue[100],
interval: 20,
divisions: 1,
subdivisions: 1,
),
drawer: Drawer(
// Add a ListView to the drawer. This ensures the user can scroll
// through the options in the drawer if there isn't enough vertical
// space to fit everything.
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('Startpunkt'),
onTap: () {
// Update the state of the app
// ...
// Then close the drawer
Navigator.pop(context);
},
),
ListTile(
title: Text('Ziel'),
onTap: () {
// Update the state of the app
// ...
// Then close the drawer
Navigator.pop(context);
},
),
],
),
),
);
}
}
LG Robsen
Since your GridPaper is defined with intervals of 20, it will be quite easy to use the localPosition of the details of an onTapDown callback provided by a GestureDetector on the whole GridPaper:
Full source code
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final appTitle = 'Path Finder';
final Color gridColor = Colors.lightBlue[100];
#override
Widget build(BuildContext context) {
return MaterialApp(
title: appTitle,
home: MyHomePage(title: appTitle),
);
}
}
class MyHomePage extends HookWidget {
final double cellSize = 20.0;
final String title;
MyHomePage({
Key key,
this.title,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final _activated = useState<List<Offset>>([]);
void _toggle(Offset offset) {
if (!_activated.value.remove(offset)) _activated.value.add(offset);
_activated.value = [..._activated.value];
}
return Scaffold(
appBar: AppBar(title: Text(title)),
body: GestureDetector(
onTapDown: (details) => _toggle(details.localPosition ~/ cellSize),
child: GridPaper(
child: Stack(
children: [
Container(color: Colors.white),
..._activated.value.map((offset) {
print('OFFSET: $offset');
return Positioned(
left: offset.dx * cellSize,
top: offset.dy * cellSize,
width: cellSize,
height: cellSize,
child: ColoredBox(color: Colors.green.shade200),
);
}).toList(),
],
),
color: Colors.lightBlue[100],
interval: cellSize,
divisions: 1,
subdivisions: 1,
),
),
drawer: Drawer(
// Add a ListView to the drawer. This ensures the user can scroll
// through the options in the drawer if there isn't enough vertical
// space to fit everything.
child: ListView(
// Important: Remove any padding from the ListView.
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('Startpunkt'),
onTap: () {
// Update the state of the app
// ...
// Then close the drawer
Navigator.pop(context);
},
),
ListTile(
title: Text('Ziel'),
onTap: () {
// Update the state of the app
// ...
// Then close the drawer
Navigator.pop(context);
},
),
],
),
),
);
}
}
I have the following slightly modified standart app, in which
the button and the result are put in a container.
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,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: containerWidget()
),
);
}
}
class containerWidget extends StatefulWidget {
#override
_containerWidgetState createState() => _containerWidgetState();
}
class _containerWidgetState extends State<containerWidget> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
height : 300,
width : 300,
child:
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
],
),
);
}
}
In a first step, I would like to get the width and size of the screen to set the relative position of the container widget within the screen.
In a second step, I would like to modify the position of the Button within the Container widget, that means either
by getting the absolute position of the container to set the absolute position of the button w.r.t the screen size
or
by setting the relative position of the button inside the container
Positioned(left: 30.0,
top: 50.0,
child: Container(
width: 100.0,
height: 80.0,
decoration: new BoxDecoration(color: Colors.red),
child: ...
For the height and width of the screen you can check ou this answer :
Flutter screen size
if you'd like to move the button relative to the parent Container, you can wrap the container with a Stack widget, and set the container and button as its children(meaning you have to move the button outside the container), then simply wrap the button with a Positioned widget and use arguments right : , left : , top : , bottom : , to control the button's position with respect to the container
//for example, this means the container is 30px to the left and 50px from the top
Positioned(left: 30.0,
top: 50.0,
child: Container()),
I am trying to implement a selectable ListTile. When ListTile is selected it's leading icon changes and its background color changes. I am trying to use AnimatedSwitcher to animate the transition when icons are changed. And it works (as long as I don't change background color of the list tile).
Changing the background color of the ListTile causes the animation to not work anymore. And I think I know why that is. When background color of the ListTile is changed, entire ListTile gets rebuilt. Which causes AnimatedSwitcher to also be rebuilt instead of transitioning between icons. Is there a way I can implement what I am trying to do. Here is my full code.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
// This widget is the root of your application.
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool change = false;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Center(
child: SomeStatelessWidget(
AnimatedSwitcher(
duration: Duration(seconds: 2),
child: change
? CircleAvatar(
key: UniqueKey(),
child: Icon(Icons.check),
)
: CircleAvatar(
key: UniqueKey(),
child: Text("A"),
),
transitionBuilder: (child, animation) {
return RotationTransition(
turns: animation,
child: child,
);
},
),
change),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.swap_horiz),
onPressed: () {
setState(() {
change = !change;
});
},
),
floatingActionButtonLocation:
FloatingActionButtonLocation.centerFloat,
));
}
}
class SomeStatelessWidget extends StatelessWidget {
final Widget child;
final bool changed;
SomeStatelessWidget(this.child, this.changed);
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(8.0),
decoration: changed
? BoxDecoration(
color: Theme.of(context).selectedRowColor,
borderRadius: BorderRadius.all(Radius.circular(8.0)),
shape: BoxShape.rectangle)
: null,
child: ListTile(
leading: child,
title: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("This is an example"),
),
),
);
}
}
I am looking for a way to style the clipboard actions without touching textTheme/button property of the main style theme. Is this even possible?
Seems like the style is directly tied to the theme. Not the best idea but if you really wanted to you would need to create a custom popup and handle all the actions yourself.
This should get you started...
Output:
Code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _controller = new TextEditingController();
final _textfieldFocusNode = new FocusNode();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.all(20.0),
child: GestureDetector(
// intercept all pointer calls
behavior: HitTestBehavior.opaque,
onTap: () {
FocusScope.of(context).requestFocus(_textfieldFocusNode);
},
onLongPress: () {
showMenu(
context: context,
// TODO: Position dynamically based on cursor or textfield
position: RelativeRect.fromLTRB(0.0, 300.0, 300.0, 0.0),
items: [
PopupMenuItem(
child: Row(
children: <Widget>[
// TODO: Dynamic items / handle click
PopupMenuItem(
child: Text(
"Paste",
style: Theme.of(context)
.textTheme
.body2
.copyWith(color: Colors.red),
),
),
PopupMenuItem(
child: Text("Select All"),
),
],
),
),
],
);
},
child: IgnorePointer(
// ensures textfield doesn't overrule GestureDetector
child: TextField(
focusNode: _textfieldFocusNode,
controller: _controller,
),
),
),
)
],
),
),
);
}
}