How to add watermark over all screens in flutter app - flutter

Most search results showed adding watermarks to images .. what i need is to add text watermark to the whole app that covers all screens or pages of the app.. such as (beta version) or (student version) so as to avoid screenshots of not yet ready version of the app.

You can use this class:
class Watarmark extends StatelessWidget {
final int rowCount;
final int columnCount;
final String text;
const Watarmark(
{Key key,
#required this.rowCount,
#required this.columnCount,
#required this.text})
: super(key: key);
#override
Widget build(BuildContext context) {
return IgnorePointer(
child: Container(
child: Column(
children: creatColumnWidgets(),
)),
);
}
List<Widget> creatRowWdiges() {
List<Widget> list = [];
for (var i = 0; i < rowCount; i++) {
final widget = Expanded(
child: Center(
child: Transform.rotate(
angle: pi / 10,
child: Text(
text,
style: TextStyle(
color: Color(0x08000000),
fontSize: 18,
decoration: TextDecoration.none),
),
)));
list.add(widget);
}
return list;
}
List<Widget> creatColumnWidgets() {
List<Widget> list = [];
for (var i = 0; i < columnCount; i++) {
final widget = Expanded(
child: Row(
children: creatRowWdiges(),
));
list.add(widget);
}
return list;
}
}
and use by this as a widget:
OverlayEntry _overlayEntry;
void addWatermark(BuildContext context, String watermark,
{int rowCount = 3, int columnCount = 10}) async {
if (_overlayEntry != null) {
_overlayEntry.remove();
}
OverlayState overlayState = Overlay.of(context);
_overlayEntry = OverlayEntry(
builder: (context) => Watarmark(
rowCount: rowCount,
columnCount: columnCount,
text: watermark,
));
overlayState.insert(_overlayEntry);
// return await _methodChannel.invokeMethod<void>("addWatermark", ['I am a watermark']);
}

Here's an example of how you can use Overlay to add something on top of you whole flutter app
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _navigatorKey = GlobalKey<NavigatorState>();
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: WillPopScope(
onWillPop: () async => !await _navigatorKey.currentState.maybePop(),
child: LayoutBuilder(
builder: (context, constraints) {
WidgetsBinding.instance.addPostFrameCallback((_) => _insertOverlay(context));
return Navigator(
key: _navigatorKey,
onGenerateRoute: (RouteSettings settings) {
switch (settings.name) {
case '/page2':
return MaterialPageRoute(builder: (_) => Page2());
default:
return MaterialPageRoute(builder: (_) => Page1(_navigatorKey));
}
},
);
},
),
),
);
}
void _insertOverlay(BuildContext context) {
return Overlay.of(context).insert(
OverlayEntry(builder: (context) {
final size = MediaQuery.of(context).size;
return Positioned(
width: 130,
height: 50,
top: size.height - 200,
left: size.width - 200,
child: Material(
color: Colors.transparent,
child: GestureDetector(
onTap: () => print('ON TAP OVERLAY!'),
child: Center (child: Container(
decoration: BoxDecoration(color: Colors.redAccent),
child: Text('BETA VERSION')
),)
),
),
);
}),
);
}
}
class Page1 extends StatelessWidget {
final GlobalKey<NavigatorState> navigatorKey;
Page1(this.navigatorKey);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.green[200],
appBar: AppBar(title: Text('Page1')),
body: Container(
alignment: Alignment.center,
child: TextButton(
child: Text('go to Page2'),
onPressed: () => navigatorKey.currentState.pushNamed('/page2'),
),
),
);
}
}
class Page2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.yellow[200],
appBar: AppBar(title: Text('back to Page1')),
body: Container(
alignment: Alignment.center,
child: Text('Page 2'),
),
);
}
}

class WatermarkLogo extends StatelessWidget {
const WatermarkLogo({super.key, required this.child});
final Widget child;
#override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: [
Positioned.fill(child: child),
IgnorePointer(
child: Center(
child: Opacity(
opacity: 0.025,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 50,
),
child: Image.asset('assets/logo.png')),
)))
],
);
}
}

Related

pass value between bottomNavigationBar views

How am I supposed to pass a value in this big mess called Flutter?
30 years old php global $var wasn't good?
All these years were to come up with setState, passed in a controller which get redeclared as a key inside a stateful widget that receive the value from a Navigator?
By the way, I tried using Navigator.push but it seems to open a completely new window, the value is there but I'd need it to show in the tab body not in a new window, below is my code:
main.dart
import 'dart:core';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter App',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomeView(),
);
}
}
class HomeView extends StatefulWidget {
#override
_HomeViewState createState() => _HomeViewState();
}
class _HomeViewState extends State<HomeView> {
final tabs = [QRViewExample(), SecondView(res: '')];
int _currentIndex = 0;
#override
void initState() {
setState(() {});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
toolbarHeight: 40.0,
elevation: 0,
centerTitle: true,
title: Text('Flutter App'),
),
body: tabs[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.red,
currentIndex: _currentIndex,
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.white.withOpacity(0.5),
items: [
BottomNavigationBarItem(
icon: Icon(Icons.qr_code),
label: 'Scan',
),
BottomNavigationBarItem(
icon: Icon(Icons.list),
label: 'List',
),
],
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
),
);
}
}
// SECOND TAB WIDGET (custom)
class SecondView extends StatelessWidget {
const SecondView({Key? key, required this.res}) : super(key: key);
final String? res;
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Text(res!),
),
);
}
}
// FIRST TAB WIDGET (qrcode)
class QRViewExample extends StatefulWidget {
const QRViewExample({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() => _QRViewExampleState();
}
class _QRViewExampleState extends State<QRViewExample> {
Barcode? result;
QRViewController? controller;
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
#override
void reassemble() {
super.reassemble();
if (Platform.isAndroid) {
controller!.pauseCamera();
}
controller!.resumeCamera();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: 500,
child: Padding(
padding: EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
Expanded(flex: 4, child: _buildQrView(context)),
Expanded(
flex: 1,
child: FittedBox(
fit: BoxFit.contain,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
if (result != null)
Text(
'Barcode Type: ${describeEnum(result!.format)} Data: ${result!.code}')
else
const Text('Scan a code'),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.toggleFlash();
setState(() {});
},
child: FutureBuilder(
future: controller?.getFlashStatus(),
builder: (context, snapshot) {
return Text('Flash: ${snapshot.data}');
},
)),
),
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.flipCamera();
setState(() {});
},
child: FutureBuilder(
future: controller?.getCameraInfo(),
builder: (context, snapshot) {
if (snapshot.data != null) {
return Text(
'Camera facing ${describeEnum(snapshot.data!)}');
} else {
return const Text('loading');
}
},
)),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.pauseCamera();
},
child: const Text('pause',
style: TextStyle(fontSize: 20)),
),
),
Container(
margin: const EdgeInsets.all(8),
child: ElevatedButton(
onPressed: () async {
await controller?.resumeCamera();
},
child: const Text('resume',
style: TextStyle(fontSize: 20)),
),
)
],
),
],
),
),
)
],
),
),
),
);
}
Widget _buildQrView(BuildContext context) {
var scanArea = (MediaQuery.of(context).size.width < 400 ||
MediaQuery.of(context).size.height < 400)
? 150.0
: 300.0;
return QRView(
key: qrKey,
onQRViewCreated: _onQRViewCreated,
overlay: QrScannerOverlayShape(
borderColor: Colors.cyanAccent,
borderRadius: 10,
borderLength: 30,
borderWidth: 10,
cutOutSize: scanArea),
onPermissionSet: (ctrl, p) => _onPermissionSet(context, ctrl, p),
);
}
void _onQRViewCreated(QRViewController controller) {
setState(() {
this.controller = controller;
});
controller.scannedDataStream.listen((scanData) {
controller.pauseCamera();
setState(() {
result = scanData;
});
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondView(res: result!.code)))
.then((value) => controller.resumeCamera());
});
}
void _onPermissionSet(BuildContext context, QRViewController ctrl, bool p) {
log('${DateTime.now().toIso8601String()}_onPermissionSet $p');
if (!p) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('no Permission')),
);
}
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
}
How am I supposed to pass a value in this big mess called Flutter?
With state management tools like InheritedWidget, InheritedModel, Provider, BloC and many more.
30 years old php global $var wasn't good? All these years were to come up with setState, passed in a controller which get redeclared as a key inside a stateful widget that receive the value from a Navigator?
Well, you shouldn't do that and it's not meant to be done like that. We can use several methods to propagate data down the widget tree. Let me explain this with InheritedWidget. But sometimes you want to go for Provider which is a wrapper class for InheritedWidget.
First we create a class named QRListModel which extends InheritedModel:
class QRListModel extends InheritedWidget {
final List<Barcode> qrList = []; // <- This holds our data
QRListModel({required super.child});
#override
bool updateShouldNotify(QRListModel oldWidget) {
return !listEquals(oldWidget.qrList, qrList);
}
static QRListModel of(BuildContext context) {
final QRListModel? result = context.dependOnInheritedWidgetOfExactType<QRListModel>();
assert(result != null, 'No QRListModel found in context');
return result!;
}
}
updateShouldNotify is a method we have to override to tell Flutter, when we want the widgets to rebuild. We want this to happen when the list changes. The of method is just a handy way to access the QRListModel.
Now wrap a parent widget of both the scan tab view and the list tab view inside QRListModel. We go for HomeView:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter App',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: QRListModel(child: HomeView()), // <- here!
);
}
}
We can take any parent widget but it should be a class where we don't call setState. Otherwise our QRListModel also gets rebuilt and our list is gone.
Now we can access QRListModel from anywhere inside the subtree. We need it here:
void _onQRViewCreated(QRViewController controller) {
setState(() {
this.controller = controller;
this.controller!.resumeCamera();
});
controller.scannedDataStream.listen((scanData) async {
controller.pauseCamera();
QRListModel.of(context).qrList.add(scanData); // <- Here we access the list
await showDialog(
context: context,
builder: (context) => SimpleDialog(
title: Text("Barcode was added!"),
children: [
Text(scanData.code!)
],
)
);
});
}
And here we read the list:
class SecondView extends StatelessWidget {
const SecondView({Key? key, required this.res}) : super(key: key);
final String? res;
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: QRListModel.of(context).qrList.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text(QRListModel.of(context).qrList[index].code ?? "NO"),
),
);
}
);
}
}
Now both pages have access to the qr list. Please do mind that a InheritedWidget can only have final fields. So if you need mutable fields, you need an additional wrapper class. We don't need it as we don't change the list but only its elements.
By the way: You shouldn't call setState inside initState. You did this here:
class _HomeViewState extends State<HomeView> {
final tabs = [QRViewExample(), SecondView(res: '')];
int _currentIndex = 0;
#override
void initState() {
setState(() {}); // <- Don't call setState inside initState!
super.initState();
}

How can I properly remove an OverlayEntry in flutter?

In my main widget tree, I have a GestureDetector that when tapped, will launch an Overlay as follows:
OverlayState? _overlayState = Overlay.of(context);
_overlayState?.insert(
OverlayEntry(
builder: (BuildContext context) {
return ShowNotificationIcon();
},
)
);
SnowNotificationIcon is actually a StatefulWidget that houses the guts of the Overlay:
class ShowNotificationIcon extends ConsumerStatefulWidget {
const ShowNotificationIcon({Key? key}) : super(key: key);
#override
_ShowNotificationIconState createState() => _ShowNotificationIconState();
}
class _ShowNotificationIconState extends ConsumerState<ShowNotificationIcon> {
void initState(){
super.initState();
}
void dispose(){
super.dispose();
}
Positioned theDropDown(){
return
Positioned(
top: 50.0,
left: 50.0,
child: Material(
color: Colors.transparent,
child:
Column(children: [
Text('Test!'),
],)),
);
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned.fill(
child: GestureDetector(
onTap: () {
/// I WANT TO REMOVE THE OVERLAY HERE
},
child: Container(
color: Colors.transparent,
),
)
),
theDropDown()
],
);
}
}
As I understand it, the overlay must be removed via a .remove() call, but since the overlay is all housed within a StatefulWidget, how can I make a .remove call on the overlay when it was opened outside of the StateWidget?
Am I missing something obvious here?
You can try this example I created for you
OverlayState? _overlayState = Overlay.of(context);
OverlayEntry? _overlayEntry;
_overlayEntry = OverlayEntry(
builder: (BuildContext context) {
return ShowNotificationIcon(entry: _overlayEntry);
},
);
_overlayState?.insert(_overlayEntry);
class ShowNotificationIcon extends ConsumerStatefulWidget {
final OverlayEntry? entry;
const ShowNotificationIcon({Key? key, this.entry}) : super(key: key);
#override
_ShowNotificationIconState createState() => _ShowNotificationIconState();
}
class _ShowNotificationIconState extends ConsumerState<ShowNotificationIcon> {
Positioned theDropDown(){
return
Positioned(
top: 50.0,
left: 50.0,
child: Material(
color: Colors.transparent,
child:
Column(children: [
Text('Test!'),
],)),
);
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned.fill(
child: GestureDetector(
onTap: () {
if (widget.entry != null){
widget.entry.remove();
}
},
child: Container(
color: Colors.transparent,
),
)
),
theDropDown()
],
);
}
}
Here, A sample Toast class. I use close button in overlay. You can use similarly.
import 'package:flutter/material.dart';
class AppDialogs {
static final AppDialogs _instance = AppDialogs.internal();
AppDialogs.internal();
factory AppDialogs() => _instance;
static void appToast(
BuildContext context, {
required Widget title,
Widget? description,
Icon? toastIcon,
Color? toastColor,
double? height,
double? width,
bool dismissibleToast = true,
}) async {
final OverlayState? overlayState = Overlay.of(context);
late OverlayEntry overlayEntry;
overlayEntry = OverlayEntry(
builder: (content) => Positioned(
height: height ?? 80,
width: width ?? 200,
top: 0,
right: 0,
child: Card(
borderOnForeground: true,
elevation: 10,
child: Stack(
children: [
ListTile(
tileColor: toastColor,
title: title,
subtitle: description,
leading: toastIcon,
),
Positioned(
top: 0,
right: 0,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () => closeOverlay(overlayEntry),
child: const Icon(
Icons.close,
size: 14,
),
),
),
),
],
))),
);
overlayState!.insert(overlayEntry);
}
static void closeOverlay(OverlayEntry overlayEntry) {
{
overlayEntry.remove();
}
}
}
class _LoginViewState extends State<LoginView> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
TextButton(
child: Text("Login"),
onPressed: (() {
AppDialogs.appToast(context, title: Text("Toast"));
}),
)
],
),
);
}

How to Navigate in Same the Page Master Detail page Flutter

I am building a master detail based app and I want to show in splitview. Trying to understand how to push data to another page in same view but couldn't. Want to cover details data in second page. How to push data?
It could be either responsive or not. But I don't want to resolve but only using set state and fill the blank in details 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
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.title,
style: TextStyle(color: Colors.black),
),
backgroundColor: Colors.transparent,
elevation: 0,
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: VerticalSplitView(
left: ListView.builder( itemCount: 12,
itemBuilder: (context, index) {
return Card(
child: InkWell(
splashColor: Colors.blue.withAlpha(30),
onTap: () {
//Navigator.push(context, MaterialPageRoute(builder: (context) => new yapiekle()) );
},
child: Container(
child: Padding(
padding: EdgeInsets.all(12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
//Center Column contents vertically,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: ListTile(
leading: Image.network("https://picsum.photos/200/300"),
title: Text("Title"),
subtitle: Text("Subtitle")),
),
//Spacer(),
],
),
),
),
),
);
}
),
right: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
),
child: Center(
child: FlutterLogo(
size: 256,
)),
),
),
),
);
}
}
class VerticalSplitView extends StatefulWidget {
final Widget left;
final Widget right;
final double ratio;
const VerticalSplitView(
{Key key, #required this.left, #required this.right, this.ratio = 0.5})
: assert(left != null),
assert(right != null),
assert(ratio >= 0),
assert(ratio <= 1),
super(key: key);
#override
_VerticalSplitViewState createState() => _VerticalSplitViewState();
}
class _VerticalSplitViewState extends State<VerticalSplitView> {
final _dividerWidth = 16.0;
//from 0-1
double _ratio;
double _maxWidth;
get _width1 => _ratio * _maxWidth;
get _width2 => (1 - _ratio) * _maxWidth;
#override
void initState() {
super.initState();
_ratio = widget.ratio;
}
#override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, BoxConstraints constraints) {
assert(_ratio <= 1);
assert(_ratio >= 0);
if (_maxWidth == null) _maxWidth = constraints.maxWidth - _dividerWidth;
if (_maxWidth != constraints.maxWidth) {
_maxWidth = constraints.maxWidth - _dividerWidth;
}
return SizedBox(
width: constraints.maxWidth,
child: Row(
children: <Widget>[
SizedBox(
width: _width1,
child: widget.left,
),
GestureDetector(
behavior: HitTestBehavior.translucent,
child: SizedBox(
width: _dividerWidth,
height: constraints.maxHeight,
child: RotationTransition(
child: Icon(Icons.drag_handle),
turns: AlwaysStoppedAnimation(0.25),
),
),
onPanUpdate: (DragUpdateDetails details) {
setState(() {
_ratio += details.delta.dx / _maxWidth;
if (_ratio > 1)
_ratio = 1;
else if (_ratio < 0.0) _ratio = 0.0;
});
},
),
SizedBox(
width: _width2,
child: widget.right,
),
],
),
);
});
}
}
I assumed that you want to change the the right page by clicking on the left card widgets. I have been developed something like this. I am using IndexedStack for render on the right side of VerticalSplitView then use provider and consumer for controlling the page to display.
First of all you need to import provider dependency in pubspec.ymal
You can replace this code below for entire of main.dart.
In main.dart you can try to replace this code. The idea is we are going to create IndexedStack that contain the Widget (Page as you prefer). Then we are going to change the index of IndexedStack by using Provider and Consumer.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test_indexed_stack/page_data.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) {
var pageData = PageData();
return pageData;
}),
],
child: 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> {
// Set required page same as list length in left of VerticalSplitView
List<Widget> pages = [Text('Page1'), Text('Page2'), Text('Page3'),
Text('Page4'), Text('Page5'), Text('Page6'), Text('Page7'),
Text('Page8'), Text('Page9'), Text('Page10'), Text('Page11'),
Text('Page12'), ];
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.title,
style: TextStyle(color: Colors.black),
),
backgroundColor: Colors.transparent,
elevation: 0,
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: VerticalSplitView(
left: ListView.builder( itemCount: 12,
itemBuilder: (context, index) {
return Card(
child: InkWell(
splashColor: Colors.blue.withAlpha(30),
onTap: () {
// Set the current page for change page on the right side.
Provider.of<PageData>(context, listen: false).setCurrentTab(index);
},
child: Container(
child: Padding(
padding: EdgeInsets.all(12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
//Center Column contents vertically,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: ListTile(
leading: Image.network("https://picsum.photos/200/300"),
title: Text("Title"),
subtitle: Text("Subtitle")),
),
//Spacer(),
],
),
),
),
),
);
}
),
right: Consumer<PageData>(
builder: (context, pageData, child) {
return Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
),
child: IndexedStack(
children: pages,
index: pageData.currentPage,
)
);
},
),
),
),
);
}
}
class VerticalSplitView extends StatefulWidget {
final Widget left;
final Widget right;
final double ratio;
const VerticalSplitView(
{Key key, #required this.left, #required this.right, this.ratio = 0.5})
: assert(left != null),
assert(right != null),
assert(ratio >= 0),
assert(ratio <= 1),
super(key: key);
#override
_VerticalSplitViewState createState() => _VerticalSplitViewState();
}
class _VerticalSplitViewState extends State<VerticalSplitView> {
final _dividerWidth = 16.0;
//from 0-1
double _ratio;
double _maxWidth;
get _width1 => _ratio * _maxWidth;
get _width2 => (1 - _ratio) * _maxWidth;
#override
void initState() {
super.initState();
_ratio = widget.ratio;
}
#override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, BoxConstraints constraints) {
assert(_ratio <= 1);
assert(_ratio >= 0);
if (_maxWidth == null) _maxWidth = constraints.maxWidth - _dividerWidth;
if (_maxWidth != constraints.maxWidth) {
_maxWidth = constraints.maxWidth - _dividerWidth;
}
return SizedBox(
width: constraints.maxWidth,
child: Row(
children: <Widget>[
SizedBox(
width: _width1,
child: widget.left,
),
GestureDetector(
behavior: HitTestBehavior.translucent,
child: SizedBox(
width: _dividerWidth,
height: constraints.maxHeight,
child: RotationTransition(
child: Icon(Icons.drag_handle),
turns: AlwaysStoppedAnimation(0.25),
),
),
onPanUpdate: (DragUpdateDetails details) {
setState(() {
_ratio += details.delta.dx / _maxWidth;
if (_ratio > 1)
_ratio = 1;
else if (_ratio < 0.0) _ratio = 0.0;
});
},
),
SizedBox(
width: _width2,
child: widget.right,
),
],
),
);
});
}
}
You need to create file for Provider and replce the code below.
import 'package:flutter/cupertino.dart';
class PageData extends ChangeNotifier{
PageData();
int _currentPage = 0;
void setCurrentTab(int index){
this._currentPage = index;
notifyListeners();
}
int get currentPage {
return this._currentPage;
}
}
Happy coding :)
if I now want to place a button inside the right widget: "add new post". This new-post-function should create a new post by copying all data from current page to the new post with a new post-ID. In the same function it then should navigate into the new post to edit copy of comment in a copy of current post. Like:
ElevatedButton(
onPressed: () {
addPost();
}
)
addPost() {
String newId = uuid.v1();
var newPost = Entry(
id: newId,
entry: entryProvider.comment,
);
firestoreService.setEntry(newPost);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PostScreen(id: newId)));
}

PushNamed issue: Type 'FillData' (a Statefulwidget) is not a subtype of type 'List<Object>'

I'm new in Flutter. I'm trying to push a List from NewData to FillData screen with pushNamed. But it said:
The following _TypeError was thrown while handling a gesture:
type 'FillData' is not a subtype of type 'List'
If i remove the comment in '/FillData', i receive null data instead. What should i do?
This is my code:
SettingNavigator
class SettingNavigator extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => Home(),
'/NewData': (context) => NewData(),
// '/FillData': (context) => FillData(), (in comment)
}
onGenerateRoute: (setting) {
if (setting.name == '/FillData') {
final ChartGroupData chartName = setting.arguments;
final List<ChartGroupData> groupNames = setting.arguments;
return MaterialPageRoute(builder: (context) {
return FillData(
chartName: chartName,
gName: groupNames,
);
});
}
return null;
},
);
}
}
NewData
import 'package:flutter/material.dart';
class NewData extends StatefulWidget {
List<ChartGroupData> groupNames;
NewData({Key key, #required this.groupNames}) : super(key: key);
#override
NewDataStage createState() => NewDataStage();
}
class NewDataStage extends State<NewData> {
TextEditingController _nameCtrl = new TextEditingController();
var textFields = <Widget>[];
var groupTECs = <TextEditingController>[];
#override
void initState() {
super.initState();
textFields.add(createCustomTextField());
}
Widget createCustomTextField() {
var groupCtrl = TextEditingController();
groupTECs.add(groupCtrl);
return Container(
padding: EdgeInsets.fromLTRB(0, 5, 0, 0),
child: Row(
children: <Widget>[
Expanded(flex: 3, child: Text("Group ${textFields.length}")),
Container(
constraints: BoxConstraints.tightFor(width: 120, height: 60),
child: TextField(
controller: groupCtrl,
),
),
],
),
);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Center(child: Text("New Chart")),
),
body: Container(
alignment: AlignmentDirectional.center,
constraints: BoxConstraints.expand(),
child: Column(
children: <Widget>[
Text(
"Your chart name",
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
),
TextField(
style: TextStyle(fontSize: 20),
controller: _nameCtrl,
),
Expanded(
flex: 3,
child: Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: textFields.length,
itemBuilder: (BuildContext context, int index) {
return textFields[index];
},
),
),
),
SizedBox(
height: 60,
width: 120,
child: RaisedButton(
onPressed: _onTapNext,
child: Text("NEXT"),
color: Colors.green,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _onTapCreate,
child: Icon(Icons.add, color: Colors.white),
shape: CircleBorder(),
),
),
);
}
void _onTapNext() {
/// Push Groups name to FillData
widget.groupNames = List<ChartGroupData>();
for (int i = 0; i < textFields.length; i++) {
var name = groupTECs[i].text;
widget.groupNames.add(ChartGroupData(name));
}
print(widget.groupNames.toString());
Navigator.pushNamed(context, '/FillData',
arguments: FillData(
gName: widget.groupNames,
chartName: ChartGroupData(_nameCtrl.text),
));
}
void _onTapCreate() {
setState(() {
textFields.add(createCustomTextField());
});
}
}
FillData
class FillData extends StatefulWidget {
final ChartGroupData chartName;
final List<ChartGroupData> gName;
FillData({Key key, #required this.chartName, #required this.gName})
: super(key: key);
#override
FillDataStage createState() => FillDataStage();
}
class FillDataStage extends State<FillData> {
void _showDialog() {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Received Data"),
content: Text(widget.chartName.toString()),
);
},
);
}
void _onTapPrintReceivedData() {
print(widget.gName);
print(widget.chartName);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Center(
child: Text("Fill your Data"),
),
),
body: Center(
child: RaisedButton(
onPressed: () {
_onTapPrintReceivedData();
_showDialog();
},
child: Text("Print Data"),
),
),
));
}
}
Class ChartGroupData
lass ChartGroupData {
final String groupNames;
ChartGroupData(this.groupNames);
#override
String toString() {
return 'Group: $groupNames';
}
}
You have 2 problems with your code:
1- you cant user routes with onGenerateRoute, because now the app doesn't know where to go, to the widget that you didn't pass anything to (inside routes) or to the widget inside the onGenerateRoute.
2- arguments is a general object that you can put whatever you want inside of it, and doing this:
final ChartGroupData chartName = setting.arguments; final
List groupNames = setting.arguments;
passes the same value to two different objects, I solved this by doing the following (it's not the best but will give you a rough idea of what you should do)
created a new object that contains the data to be passed:
class ObjectToPass {
final ChartGroupData chartName;
final List<ChartGroupData> groupNames;
ObjectToPass({this.chartName, this.groupNames});
}
changed FillData implementation:
class FillData extends StatefulWidget {
final ObjectToPass objectToPass;
FillData({Key key, #required this.objectToPass}) : super(key: key);
#override
FillDataStage createState() => FillDataStage();
}
...
void _showDialog() {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text("Received Data"),
content: Text(widget.objectToPass.chartName.toString()),
);
},
);
}
void _onTapPrintReceivedData() {
print(widget.objectToPass.groupNames);
print(widget.objectToPass.chartName);
}
to navigate to FillData you would:
Navigator.pushNamed(
context,
'/FillData',
arguments: ObjectToPass(
chartName: ChartGroupData(_nameCtrl.text),
groupNames: groupNames,
),
);
finally this is how your MaterialApp should look like:
return MaterialApp(
initialRoute: '/NewData',
onGenerateRoute: (setting) {
if (setting.name == '/FillData') {
return MaterialPageRoute(builder: (context) {
return FillData(
objectToPass: setting.arguments,
);
});
} else if (setting.name == '/NewData') {
return MaterialPageRoute(builder: (_) => NewData());
}
return null;
},
);
you can pass a list instead of the object I created and get your objects from it by it's index.

How to call setState (rebuild page), after pop of PopUpWindow?

I'm trying to update/rebuild a route beneath a popup window when the popup window is popped, using Navigator.pop(context). On the popup window the user can add something to a list, and the route beneath (the page), the added item shows in a ListView. So more specifically I need my ListView to be up to date with the actual list. But nothing I have tried yet, have worked.
As mentioned, I have already tried quite a few things, including:
didPopNext() (using RouteAware), changedExternalState (which I couldn't quite get the grasp of), trying to pass a function only containing a setState(() {}), and other things.
Hope somebody can help with how I can rebuild the ListView when the popup window is popped.
Thanks in advance.
This example should summarize my problem decently close (couldn't show you the actual code, because it's gotten way over 2.000 lines, but the core elements of the problem should be here):
import 'package:flutter/material.dart';
void main() => runApp(new MaterialApp(
home: MyPage()));
class PopupLayout extends ModalRoute<void> {
#override
Duration get transitionDuration => Duration(milliseconds: 300);
#override
bool get opaque => false;
#override
bool get barrierDismissible => false;
#override
bool get semanticsDismissible => true;
#override
Color get barrierColor =>
bgColor == null ? Colors.black.withOpacity(0.5) : bgColor;
#override
String get barrierLabel => null;
#override
bool get maintainState => false;
double top;
double bottom;
double left;
double right;
Color bgColor;
final Widget child;
PopupLayout(
{Key key,
this.bgColor,
#required this.child,
this.top,
this.bottom,
this.left,
this.right});
#override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
if (top == null) this.top = 10;
if (bottom == null) this.bottom = 20;
if (left == null) this.left = 20;
if (right == null) this.right = 20;
return GestureDetector(
onTap: () {},
child: Material(
type: MaterialType.transparency,
child: _buildOverlayContent(context),
),
);
}
Widget _buildOverlayContent(BuildContext context) {
return Container(
margin: EdgeInsets.only(
bottom: this.bottom,
left: this.left,
right: this.right,
top: this.top),
child: child,
);
}
#override
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return FadeTransition(
opacity: animation,
child: ScaleTransition(
scale: animation,
child: child,
),
);
}
}
class PopupContent extends StatefulWidget {
final Widget content;
PopupContent({
Key key,
this.content,
}) : super(key: key);
_PopupContentState createState() => _PopupContentState();
}
class _PopupContentState extends State<PopupContent> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
child: widget.content,
);
}
}
popupContent(BuildContext context, Widget widget,
{BuildContext popupContext}) {
Navigator.push(
context,
PopupLayout(
top: 120,
left: 20,
right: 20,
bottom: 120,
child: PopupContent(
content: Scaffold(
backgroundColor: Colors.white,
resizeToAvoidBottomInset: false,
body: widget,
),
),
),
);
}
Widget popupWidget(BuildContext context) {
return new Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("This is a popupPage"),
FlatButton(
onPressed: () {
list.add("Irrelevant");
Navigator.pop(context); //The rebuild of the page underneath, needs to be called when this pops
},
child: Text("Press me to change text on home page"),
),
]
),
);
}
List list = [];
class MyPage extends StatefulWidget {
#override
createState() => MyPageState();
}
class MyPageState extends State<MyPage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: Container(
width: 300,
height: 400,
child: ListView.builder(
itemCount: list.length,
itemBuilder: (BuildContext context, int index) {
return Center(
child: Text(index.toString()),
);
},
),
),
),
Center(
child: FlatButton(
onPressed: () {
popupContent(context, popupWidget(context));
},
child: Text("Press me for popup window"),
),
),
Center(
child: FlatButton(
onPressed: () {
setState(() {});
},
child: Text("Press me to rebuild page (setState)"),
),
),
]
),
);
}
}
You can add a function in MyPage and pass it to your popUpWidget. Code:
import 'package:flutter/material.dart';
void main() => runApp(new MaterialApp(home: MyPage()));
class PopupLayout extends ModalRoute<void> {
#override
Duration get transitionDuration => Duration(milliseconds: 300);
#override
bool get opaque => false;
#override
bool get barrierDismissible => false;
#override
bool get semanticsDismissible => true;
#override
Color get barrierColor =>
bgColor == null ? Colors.black.withOpacity(0.5) : bgColor;
#override
String get barrierLabel => null;
#override
bool get maintainState => false;
double top;
double bottom;
double left;
double right;
Color bgColor;
final Widget child;
PopupLayout(
{Key key,
this.bgColor,
#required this.child,
this.top,
this.bottom,
this.left,
this.right});
#override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
if (top == null) this.top = 10;
if (bottom == null) this.bottom = 20;
if (left == null) this.left = 20;
if (right == null) this.right = 20;
return GestureDetector(
onTap: () {},
child: Material(
type: MaterialType.transparency,
child: _buildOverlayContent(context),
),
);
}
Widget _buildOverlayContent(BuildContext context) {
return Container(
margin: EdgeInsets.only(
bottom: this.bottom,
left: this.left,
right: this.right,
top: this.top),
child: child,
);
}
#override
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return FadeTransition(
opacity: animation,
child: ScaleTransition(
scale: animation,
child: child,
),
);
}
}
class PopupContent extends StatefulWidget {
final Widget content;
PopupContent({
Key key,
this.content,
}) : super(key: key);
_PopupContentState createState() => _PopupContentState();
}
class _PopupContentState extends State<PopupContent> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
child: widget.content,
);
}
}
popupContent(BuildContext context, Widget widget, {BuildContext popupContext}) {
Navigator.push(
context,
PopupLayout(
top: 120,
left: 20,
right: 20,
bottom: 120,
child: PopupContent(
content: Scaffold(
backgroundColor: Colors.white,
resizeToAvoidBottomInset: false,
body: widget,
),
),
),
);
}
Widget popupWidget(BuildContext context, Function callback) {
//Pass The Function Here //////////////////////
return new Container(
child:
Column(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
Text("This is a popupPage"),
FlatButton(
onPressed: () {
list.add("Irrelevant");
callback(); // Call It Here /////////////////////////
Navigator.pop(
context); //The rebuild of the page underneath, needs to be called when this pops
},
child: Text("Press me to change text on home page"),
),
]),
);
}
List list = [];
class MyPage extends StatefulWidget {
#override
createState() => MyPageState();
}
class MyPageState extends State<MyPage> {
//The Calback Function //////////////////////////
void callback() {
setState(() {});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: Container(
width: 300,
height: 400,
child: ListView.builder(
itemCount: list.length,
itemBuilder: (BuildContext context, int index) {
return Center(
child: Text(index.toString()),
);
},
),
),
),
Center(
child: FlatButton(
onPressed: () {
popupContent(context, popupWidget(context, this.callback)); //Passing the Function here ////////////
},
child: Text("Press me for popup window"),
),
),
Center(
child: FlatButton(
onPressed: () {
setState(() {});
},
child: Text("Press me to rebuild page (setState)"),
),
),
]),
);
}
}