Align widget center with AppBar bottom edge - flutter

I need to align a widget's center with the appbar bottom edge.
So it will be located vertically half on the appbar and half on the page body.
Right now I've added the widget into the AppBar bottom: but it wont align with it's horizontal center line.
Currently It looks like this:
While i want that the center of the SelectEnvironment button along with the horizontal white line will 'sit' exactly on the bottom edge of the appBar
The code for the appBar is like this:
class CustomAppBar extends AppBar {
final Widget appBarActionButton;
CustomAppBar({Widget title = AppUtils.EMPTY_TEXT_VIEW, this.appBarActionButton}): super(
title: title,
backgroundColor: Colors.blueGrey,
elevation: 0,
bottom: PreferredSize(
child: Stack( //The stack holds the horizontal line and the button aligned cente
alignment: Alignment.center,
children: <Widget>[
Container( //This is the horizontal line
color: Colors.GeneralDividerGray,
height: 1.0,
),
Align(
child: Container(
child: appBarActionButton, //This is the button widget
),
)
],
),
preferredSize: Size.fromHeight(4.0)),
);
}
If there a better way to achieve this by taking it outside of the appbar it's ok with me as long it will give the same effect.

I think you should use Stack and Column like this
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
typedef void OnWidgetSizeChange(Size size);
class MeasureSize extends StatefulWidget {
final Widget child;
final OnWidgetSizeChange onChange;
const MeasureSize({
Key key,
#required this.onChange,
#required this.child,
}) : super(key: key);
#override
_MeasureSizeState createState() => _MeasureSizeState();
}
class _MeasureSizeState extends State<MeasureSize> {
#override
Widget build(BuildContext context) {
SchedulerBinding.instance.addPostFrameCallback(postFrameCallback);
return Container(
key: widgetKey,
child: widget.child,
);
}
var widgetKey = GlobalKey();
var oldSize;
void postFrameCallback(_) {
var context = widgetKey.currentContext;
if (context == null) return;
var newSize = context.size;
if (oldSize == newSize) return;
oldSize = newSize;
widget.onChange(newSize);
}
}
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> {
Size s;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Column(
children: [
MeasureSize(
onChange: (size) {
setState(() {
s = size;
});
},
child: AppBar(
title: Text('title'),
),
),
SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height - (s?.height ?? 0.0),
child: Center(child: Text('body')))
],
),
Positioned(
top: (s?.height ?? 0.0) - 16.0,
child: Container(
width: MediaQuery.of(context).size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 32,
color: Colors.red[400],
padding: EdgeInsets.all(6),
child: Center(child: Text('Select Environment'))),
],
),
),
)
],
));
}
}

The best way is to use Slivers via a widget like below:
ScrollController scrollController = new ScrollController();
return Stack(
children: [
NestedScrollView(
controller: scrollController,
headerSliverBuilder: (context, value){
return [
// list of widgets in here
];
},
body: Container(
// here, your normal body goes
),
),
Positioned(
top: 50.0,
left: 100.0,
child: Container(
// your centered widget here
),
)
]
);
}
Instead of using a normal appBar, you have to use a SliverAppBar

Related

How can i make a moveable overlay?

I want to show a minimize moveable calling screen in top of the app
I tried with stack it does not meet my expectation
#Raiyan, you have to use picture-in-picture concept to implement such floating child.
In flutter, multiple plugins are there, that we can use for the, some are as follows:
https://pub.dev/packages/pip_view
https://pub.dev/packages/floating
https://pub.dev/packages/easy_pip
floating package will fit in your case, it provides picture in Picture mode management for Flutter.
Sadly the gif is not working... But by on taping and draging on the green window will make the green window move.
Try this:
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.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({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return OverlayWindow(
overlayChild: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
"Overlay Window",
style: TextStyle(fontSize: 20),
),
Icon(
Icons.android,
size: 80,
),
],
),
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
),
);
}
}
class OverlayWindow extends StatefulWidget {
const OverlayWindow(
{Key? key, required this.overlayChild, required this.child})
: super(key: key);
final Widget overlayChild;
final Widget child;
#override
State<OverlayWindow> createState() => _OverlayWindowState();
}
class _OverlayWindowState extends State<OverlayWindow> {
double _top = 0;
double _left = 0;
#override
Widget build(BuildContext context) {
return Stack(
children: [
widget.child,
Positioned(
top: _top,
left: _left,
child: GestureDetector(
onPanUpdate: (details) {
setState(() {
_top = max(0, _top + details.delta.dy);
_left = max(0, _left + details.delta.dx);
});
},
child: Container(
height: 300,
width: 200,
color: Colors.green,
child: widget.overlayChild,
),
),
)
],
);
}
}
More about things like that, you can find here:
https://docs.flutter.dev/development/ui/advanced/gestures

How to create interactive flowchart in flutter

I want to create a flutter UI where there are some shapes like square, rectangle, circle, arrow. And I must be able to drag and drop them at the centre and add text to it and connect them. I have just started with flutter so I am not sure how to do this. Can anyone please help me?
Use Draggable class
Example:
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> {
int acceptedData = 0;
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Draggable<int>(
// Data is the value this Draggable stores.
data: 10,
feedback: Container(
color: Colors.deepOrange,
height: 100,
width: 100,
child: const Icon(Icons.directions_run),
),
childWhenDragging: Container(
height: 100.0,
width: 100.0,
color: Colors.pinkAccent,
child: const Center(
child: Text('Child When Dragging'),
),
),
child: Container(
height: 100.0,
width: 100.0,
color: Colors.lightGreenAccent,
child: const Center(
child: Text('Draggable'),
),
),
),
DragTarget<int>(
builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
return Container(
height: 100.0,
width: 100.0,
color: Colors.cyan,
child: Center(
child: Text('Value is updated to: $acceptedData'),
),
);
},
onAccept: (int data) {
setState(() {
acceptedData += data;
});
},
),
],
);
}
}

how to get a grid element accessible in flutter by clicking on it?

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);
},
),
],
),
),
);
}
}

How to add a fixed bar between a sliver bar and the scrolling content

I have a view in my flutter mobile app in which there is:
A top bar that is a sliver bar (title + tabs) [red box on image]
A scrollable list of data [green box on image]
When i scroll, the tabs disappear leaving only the page title.
Goal:
I want to add a header [blue box on image] that does not disappear when i scroll and stays between the sliver bar and the scrollable items.
I seem to only be able to put the header inside the title of the sliver bar above the tabs, or in the scrollable list in which case it disappears as i scroll
You can easily implement this on your own. The widget you will need is called SliverPersistentHeader with pinned parameter equal to true and custom implementation of SliverPersistentHeaderDelegate.
Take a look at this example:
https://dartpad.dartlang.org/6e0f6b4a212e572c7b74e5f301abc71b
import 'dart:math' as math;
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
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> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: CustomScrollView(
slivers: <Widget>[
SliverPersistentHeader(
floating: true,
delegate: SliverAppBarDelegate(
minHeight: 60,
maxHeight: 60,
child: Container(
color: Colors.red,
child: Center(
child: Text(
'This will hide'),
),
),
),
),
SliverPersistentHeader(
pinned: true,
delegate: SliverAppBarDelegate(
minHeight: 60.0,
maxHeight: 60.0,
child: Container(
color: Theme.of(context).scaffoldBackgroundColor,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
child: Text('This will remain visible',
style: TextStyle(fontSize: 20)),
),
Divider(),
],
),
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return ListTile(
title: Text('List Tile $index'),
);
}, childCount: 100),
),
],
),
);
}
}
class SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
SliverAppBarDelegate({
#required this.minHeight,
#required this.maxHeight,
#required this.child,
});
final double minHeight;
final double maxHeight;
final Widget child;
#override
double get minExtent => minHeight;
#override
double get maxExtent => math.max(maxHeight, minHeight);
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return SizedBox.expand(child: child);
}
#override
bool shouldRebuild(SliverAppBarDelegate oldDelegate) {
return maxHeight != oldDelegate.maxHeight ||
minHeight != oldDelegate.minHeight ||
child != oldDelegate.child;
}
}
Ended up using a sticky header: https://pub.dev/packages/flutter_sticky_header
And keep the same header without changing it through the scroll

How to autocorrect scroll position in a listview? (flutter)

I have a horizontal listviewbuilder as shown in the picture on the top:
Is there a way to autocorrect the current position so that the widget on the right corner fits perfectly to his full width? (here number 27)Because if you stop scrolling, it doesn't look nice if it is cut out like the number 27 in the picture. So is it possible to stop only on the fullest itemwidget so that the current position will not be like in the picture?
Here is my code for the listview builder:
import './date_widget.dart';
import 'package:date_picker_timeline/gestures/tap.dart';
import 'package:flutter/material.dart';
import 'package:intl/date_symbol_data_local.dart';
class MyDatePickerTimeline extends StatefulWidget {
DateTime currentDate;
DateChangeListener onDateChange;
String locale;
// Creates the DatePickerTimeline Widget
MyDatePickerTimeline(
this.currentDate, {
Key key,
this.onDateChange,
this.locale = "de",
}) : super(key: key);
#override
State<StatefulWidget> createState() => new _MyDatePickerTimelineState();
}
class _MyDatePickerTimelineState extends State<MyDatePickerTimeline> {
#override void initState() {
super.initState();
initializeDateFormatting(widget.locale, null);
}
#override
Widget build(BuildContext context) {
return Container(
// padding: EdgeInsets.only(bottom: 600),
width: MediaQuery.of(context).size.width ,
height: 200,
child: ListView.builder(
reverse: true,
itemCount: 5000,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
// Return the Date Widget
DateTime _date = DateTime.now().subtract(Duration(days: index));
DateTime date = new DateTime(_date.year, _date.month, _date.day);
bool isSelected = compareDate(date, widget.currentDate);
return MyDateWidget(
date: date,
locale: widget.locale,
selectionColor:
isSelected ? Colors.black12 : Colors.transparent,
onDateSelected: (selectedDate) {
// A date is selected
if (widget.onDateChange != null) {
widget.onDateChange(selectedDate);
}
setState(() {
widget.currentDate = selectedDate;
});
},
);
},
),
);
}
bool compareDate(DateTime date1, DateTime date2) {
return date1.day == date2.day &&
date1.month == date2.month &&
date1.year == date2.year;
}
}
Or is it possible to set a fixed width that you can scroll?
It seems that you are looking for PageScrollPhysics. From the PageScrollPhysics class documentation:
Scroll physics used by a PageView. These physics cause the page view to snap to page boundaries.
Having your ListView's physics property set to PageScrollPhysics will make the list scroll in a paginated, discrete way. If you also set the width of the widgets inside your ListView equal a fraction of the screen width, then the widgets inside the ListView will never be cut, no matter how many items there are in the list, how big the screen gets, or how the user scrolls.
Check out this sample I wrote to show you a way to implement this kind of scroll physics. You can copy it and run it in DartPad to see if it is what you are looking for. Note that there are 3 entries per page scrolled, yet there are 7 entries total in the ListView, and there is no way to have any of them cut in the view.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
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> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView(
physics: PageScrollPhysics(),
scrollDirection: Axis.horizontal,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width/3,
color: Colors.amber[900],
child: const Center(child: Text('Entry A')),
),
Container(
width: MediaQuery.of(context).size.width/3,
color: Colors.amber[800],
child: const Center(child: Text('Entry B')),
),
Container(
width: MediaQuery.of(context).size.width/3,
color: Colors.amber[700],
child: const Center(child: Text('Entry C')),
),
Container(
width: MediaQuery.of(context).size.width/3,
color: Colors.amber[600],
child: const Center(child: Text('Entry D')),
),
Container(
width: MediaQuery.of(context).size.width/3,
color: Colors.amber[500],
child: const Center(child: Text('Entry E')),
),
Container(
width: MediaQuery.of(context).size.width/3,
color: Colors.amber[400],
child: const Center(child: Text('Entry F')),
),
Container(
width: MediaQuery.of(context).size.width/3,
color: Colors.amber[300],
child: const Center(child: Text('Entry G')),
),
],
),
);
}
}