after adding SliverAppBar to DraggableScrollableSheet i cant scroll sheet to up or down of screen, but list inside SliverAppbar work fine,
full source code:
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(
home: HomePage(),
));
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
AnimationController _controller;
BorderRadiusTween borderRadius;
Duration _duration = Duration(milliseconds: 500);
Tween<Offset> _tween = Tween(begin: Offset(0, 1), end: Offset(0, 0));
double _height, min = 0.1, initial = 0.3, max = 1;
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: _duration);
borderRadius = BorderRadiusTween(
begin: BorderRadius.circular(10.0),
end: BorderRadius.circular(0.0),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text('DraggableScrollableSheet'),
),
floatingActionButton: GestureDetector(
child: FloatingActionButton(
child: AnimatedIcon(icon: AnimatedIcons.menu_close, progress: _controller),
elevation: 5,
backgroundColor: Colors.deepOrange,
foregroundColor: Colors.white,
onPressed: () async {
if (_controller.isDismissed)
_controller.forward();
else if (_controller.isCompleted) _controller.reverse();
},
),
),
body: SizedBox.expand(
child: Stack(
children: <Widget>[
FlutterLogo(size: 500),
SizedBox.expand(
child: SlideTransition(
position: _tween.animate(_controller),
child: DraggableScrollableSheet(
minChildSize: min, // 0.1 times of available height, sheet can't go below this on dragging
maxChildSize: max, // 0.7 times of available height, sheet can't go above this on dragging
initialChildSize: initial, // 0.1 times of available height, sheet start at this size when opened for first time
builder: (BuildContext context, ScrollController controller) {
if (controller.hasClients) {
var dimension = controller.position.viewportDimension;
_height ??= dimension / initial;
if (dimension >= _height * max * 0.9)
_onWidgetDidBuild(() {
_scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text('ON TOP'),
duration: Duration(seconds: 3),
));
});
else if (dimension <= _height * min * 1.1)
_onWidgetDidBuild(() {
_scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text('ON BOTTOM'),
duration: Duration(seconds: 3),
));
});
}
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
return ClipRRect(
borderRadius: borderRadius.evaluate(CurvedAnimation(parent: _controller, curve: Curves.ease)),
child: Container(
color: Colors.blue[800],
child: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
title: Text("What's Up?"),
backgroundColor: Colors.orange,
automaticallyImplyLeading: false,
primary: false,
floating: true,
pinned: true,
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, idx) => ListTile(
title: Text("Nothing much"),
subtitle: Text("$idx"),
),
childCount: 100,
),
)
],
),
),
);
},
);
},
),
),
),
],
),
),
);
}
_onWidgetDidBuild(Function callback) {
WidgetsBinding.instance.addPostFrameCallback((_) {
callback();
});
}
}
CustomScrollView(
controller: controller, // you missed this
...
)
Related
So, I built a website using flutter web. Everything works fine when I run using flutter run command, the onUnknownRoute gets triggered when I type something in the URL that does not exist. But when I build my project using the flutter build web command, the onUnknownRoute never gets triggered. I have uploaded my project on github pages but onUnknownRoute never gets triggered. I don't even understand what the error could be so, I don't know what exactly should I be posting here as the error-prone code. I shall be delighted if I could get some help. Thank You.
The main.dart
import 'package:flutter/material.dart';
import 'package:app/constants.dart';
import 'package:app/provider/themeProvider.dart';
import 'package:app/provider/themeStyles.dart';
import 'package:app/sections/error404.dart';
import 'package:app/sections/getInTouch/getInTouch.dart';
import 'package:app/sections/mainSection.dart';
import 'package:app/sections/serviceDetails/serviceDetails.dart';
import 'package:app/utils/routeConfiguration.dart';
import 'package:provider/provider.dart';
import 'package:url_strategy/url_strategy.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
setPathUrlStrategy();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ThemeProvider _themeProvider = ThemeProvider();
void getCurrentAppTheme() async {
_themeProvider.lightTheme = await _themeProvider.darkThemePref.getTheme();
}
#override
void initState() {
getCurrentAppTheme();
super.initState();
}
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => ThemeProvider()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'App',
theme: ThemeStyles.themeData(_themeProvider.lightTheme, context),
onGenerateRoute: RouteConfiguration.onGenerateRoute,
initialRoute: MainPage.Route,
onUnknownRoute: (settings) {
return CustomPageRoute(
builder: (_) => ErrorPage(),
// ignore: prefer_const_constructors
settings: RouteSettings(name: ErrorPage.Route));
},
),
);
}
}
The routeConfiguration.dart
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:app/sections/error404.dart';
import 'package:app/sections/mainSection.dart';
import 'package:app/utils/Path.dart';
class RouteConfiguration {
/// List of [Path] to for route matching. When a named route is pushed with
/// [Navigator.pushNamed], the route name is matched with the [Path.pattern]
/// in the list below. As soon as there is a match, the associated builder
/// will be returned. This means that the paths higher up in the list will
/// take priority.
static List<Path> paths = [
Path(
r'^' + ErrorPage.Route + '\$',
(context, match) => ErrorPage(),
),
Path(
r'^' + ErrorPage.Route + '/\$',
(context, match) => ErrorPage(),
),
];
/// The route generator callback used when the app is navigated to a named
/// route. Set it on the [MaterialApp.onGenerateRoute] or
/// [WidgetsApp.onGenerateRoute] to make use of the [paths] for route
/// matching.
static Route<dynamic> onGenerateRoute(RouteSettings settings) {
if (settings.name == "/") {
return CustomPageRoute(
builder: (context) => MainPage(),
settings: settings,
);
}
for (Path path in paths) {
final regExpPattern = RegExp(path.pattern!);
if (regExpPattern.hasMatch(settings.name!)) {
final firstMatch = regExpPattern.firstMatch(settings.name!);
final match = (firstMatch!.groupCount == 1) ? firstMatch.group(1) : null;
return CustomPageRoute(
builder: (context) {
return path.builder(context, match);
},
settings: RouteSettings(name: ErrorPage.Route),
);
}
}
// If no match was found, we let [WidgetsApp.onUnknownRoute] handle it.
return CustomPageRoute(
builder: (_) => ErrorPage(),
settings: RouteSettings(name: ErrorPage.Route));
}
}
class CustomPageRoute extends PageRouteBuilder {
final Widget Function(dynamic)? builder;
final RouteSettings settings;
CustomPageRoute({this.builder, required this.settings})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) =>
builder!.call(context),
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) =>
FadeTransition(
opacity: animation,
child: ScaleTransition(
scale: Tween<double>(
begin: 0.5,
end: 1.0,
).animate(
CurvedAnimation(
parent: animation,
curve: Curves.fastOutSlowIn,
),
),
child: child,
),
),
settings: settings);
}
the error404.dart
// ignore_for_file: prefer_const_constructors
import 'dart:ui';
import 'package:flutter/rendering.dart';
import 'package:app/animations/glitchAnimation.dart';
import 'package:app/provider/themeProvider.dart';
import 'package:app/sections/mainSection.dart';
import 'package:app/utils/animatedUFO.dart';
import 'package:app/utils/star_field.dart';
import 'package:app/widget/adaptiveText.dart';
import 'package:mouse_parallax/mouse_parallax.dart';
import 'package:provider/provider.dart';
import 'package:seo_renderer/seo_renderer.dart';
import 'package:flutter/material.dart';
import 'package:app/animations/entranceFader.dart';
import 'package:app/sections/navBar/navBarLogo.dart';
class ErrorPage extends StatefulWidget {
static const String Route = "/404";
#override
_ErrorPageState createState() => _ErrorPageState();
}
class _ErrorPageState extends State<ErrorPage>
with SingleTickerProviderStateMixin {
final globalKey = GlobalKey<ScaffoldState>();
ThemeProvider _themeProviders = ThemeProvider();
bool isPressed = false;
bool _isScrollingDown = false;
ScrollController _scrollController = ScrollController();
static const double idleSpeed = .2;
static const int starAnimDurationIn = 4500;
ValueNotifier<double> _speedValue = ValueNotifier(idleSpeed);
late AnimationController _starAnimController;
late Animation<double> _starAnimSequence;
#override
void initState() {
_scrollController = _themeProviders.scroll;
_scrollController.addListener(() {
if (_scrollController.position.userScrollDirection ==
ScrollDirection.reverse) {
if (!_isScrollingDown) {
_isScrollingDown = true;
setState(() {});
}
}
if (_scrollController.position.userScrollDirection ==
ScrollDirection.forward) {
if (_isScrollingDown) {
_isScrollingDown = false;
setState(() {});
}
}
});
_starAnimController = AnimationController(
vsync: this,
duration: Duration(milliseconds: starAnimDurationIn),
reverseDuration: Duration(milliseconds: starAnimDurationIn ~/ 3),
);
_starAnimController.addListener(() {
_speedValue.value = _starAnimSequence.value;
});
//Create an animation sequence that moves our stars back, then forwards, then to rest at 0.
//This will be played each time we load a detail page, to create a flying through space transition effect
_starAnimSequence = TweenSequence([
TweenSequenceItem<double>(
tween: Tween<double>(begin: idleSpeed, end: -2)
.chain(CurveTween(curve: Curves.easeOut)),
weight: 20.0,
),
TweenSequenceItem<double>(
tween: Tween<double>(begin: -2, end: 20)
.chain(CurveTween(curve: Curves.easeOut)),
weight: 30.0,
),
TweenSequenceItem<double>(
tween: Tween<double>(begin: 20, end: 0)
.chain(CurveTween(curve: Curves.easeOut)),
weight: 50.0,
)
]).animate(_starAnimController);
super.initState();
// WidgetsBinding.instance?.addPostFrameCallback((_) {
// if (regExpBots.hasMatch(window.navigator.userAgent.toString()) && (globalKey.currentState != null)) {
// globalKey.currentState.openDrawer();
// }
// });
}
#override
void dispose() {
_scrollController.dispose();
_scrollController.removeListener(() {});
_starAnimController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final _themeProv = Provider.of<ThemeProvider>(context);
double _height = MediaQuery.of(context).size.height;
int starCount = 400;
return Container(
child: Stack(
children: [
Container(
width: double.maxFinite,
height: double.maxFinite,
child: ImageRenderer(
alt: 'Background Image For Decoration',
link: 'assets/logos/i8.jpg',
child: Image.asset(
'assets/logos/i8.jpg',
fit: BoxFit.cover,
),
),
),
BackdropFilter(
filter: ImageFilter.blur(
sigmaX: 30.0,
sigmaY: 30.0,
),
child: Scaffold(
key: globalKey,
extendBodyBehindAppBar: true,
backgroundColor: _themeProv.lightTheme
? Colors.white
: Colors.black.withOpacity(0.0),
appBar: (MediaQuery.of(context).size.width < 860)
? AppBar(
automaticallyImplyLeading: false,
iconTheme: IconThemeData(
color: _themeProv.lightTheme
? Color(0xff1C1C28)
: Colors.white),
elevation: 0,
backgroundColor: Colors.transparent,
actions: [
GestureDetector(
onTap: () {
Navigator.pushNamedAndRemoveUntil(
context, MainPage.Route, (c) {
return false;
});
},
child: NavBarLogo(),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.05,
)
],
)
: _appBarTabDesktop(_themeProv),
body: Stack(
children: [
ValueListenableBuilder<double>(
valueListenable: _speedValue,
builder: (context, value, child) {
//Scrolling star background
return StarField(starSpeed: value, starCount: starCount);
},
),
Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 27.0),
child: ParallaxStack(
resetOnExit: true,
useLocalPosition: true,
referencePosition: 0.6,
dragCurve: Curves.easeIn,
resetCurve: Curves.bounceOut,
layers: [
ParallaxLayer(
yRotation: 0.80,
xRotation: 0.80,
xOffset: 90,
yOffset: 80,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: TextRenderer(
text: GlithEffect(
child: AdaptiveText(
"404",
style: TextStyle(
fontSize: _height * 0.1,
fontFamily: 'Montserrat',
fontWeight: FontWeight.w900,
color: Colors.white,
),
),
),
),
),
SizedBox(
height: _height * 0.02,
),
TextRenderer(
text: AdaptiveText(
"We searched as far as we could, but were unable to find the page you were looking for.\nSorry.",
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: 'Ubuntu',
fontSize: _height * 0.02,
color: Colors.white,
height: 2.0,
),
),
),
],
),
),
],
),
),
),
],
),
),
),
],
),
);
}
AppBar _appBarTabDesktop(ThemeProvider _themeProv) {
return AppBar(
elevation: 0.0,
backgroundColor:
_themeProv.lightTheme ? Colors.white : Colors.transparent,
automaticallyImplyLeading: false,
centerTitle: false,
title: MediaQuery.of(context).size.width < 780
? EntranceFader(
duration: Duration(milliseconds: 250),
offset: Offset(0, -10),
delay: Duration(seconds: 3),
child: GestureDetector(
onTap: () {
Navigator.pushNamedAndRemoveUntil(context, MainPage.Route,
(c) {
return false;
});
},
child: NavBarLogo(
height: 37.0,
),
))
: EntranceFader(
offset: Offset(0, -10),
duration: Duration(milliseconds: 250),
delay: Duration(milliseconds: 100),
child: GestureDetector(
onTap: () {
Navigator.pushNamedAndRemoveUntil(context, MainPage.Route,
(c) {
return false;
});
},
child: NavBarLogo(
height: MediaQuery.of(context).size.height * 0.065,
),
),
),
);
}
}
Also, I read in some other solution for a custom 404 page for github pages that I need a custom domain for it to work. So, is that what is causing my problem.
You should use Navigator v2 for web. it has more features and makes working with web routes very easy.
how can I transition between pages with 3D/Cube effects? I need that transition looks like this
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 3D transition',
theme: ThemeData(
backgroundColor: Colors.white,
primarySwatch: Colors.green,
),
home: Page1());
}
}
class Page1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blue[50],
body: Center(
child: RaisedButton(
onPressed: () => Navigator.of(context).pushReplacement(
Pseudo3dRouteBuilder(this, Page2()),
),
child: Text('change the page'),
),
),
);
}
}
class Page2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.pink[50],
body: Center(
child: RaisedButton(
onPressed: () => Navigator.of(context).pushReplacement(
Pseudo3dRouteBuilder(this, Page1()),
),
child: Text('change the page'),
),
),
);
}
}
class Pseudo3dRouteBuilder extends PageRouteBuilder {
final Widget enterPage;
final Widget exitPage;
Pseudo3dRouteBuilder(this.exitPage, this.enterPage)
: super(
pageBuilder: (context, animation, secondaryAnimation) => enterPage,
transitionsBuilder: _transitionsBuilder(exitPage, enterPage),
);
static _transitionsBuilder(exitPage, enterPage) =>
(context, animation, secondaryAnimation, child) {
return Stack(
children: <Widget>[
SlideTransition(
position: Tween<Offset>(
begin: Offset.zero,
end: Offset(-1.0, 0.0),
).animate(animation),
child: Container(
color: Colors.white,
child: Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.003)
..rotateY(pi / 2 * animation.value),
alignment: FractionalOffset.centerRight,
child: exitPage,
),
),
),
SlideTransition(
position: Tween<Offset>(
begin: Offset(1.0, 0.0),
end: Offset.zero,
).animate(animation),
child: Container(
color: Colors.white,
child: Transform(
transform: Matrix4.identity()
..setEntry(3, 2, 0.003)
..rotateY(pi / 2 * (animation.value - 1)),
alignment: FractionalOffset.centerLeft,
child: enterPage,
),
),
)
],
);
};
}
I've just released a package that has the animation you want. It's called cube_transition.
Thanks #Kherel, I used your code for part of my package.
You can check it : https://pub.dev/packages/cube_transition
Using my package the code is very simple.
import 'package:cube_transition/cube_transition.dart';
Navigator.of(context).push(
CubePageRoute(
enterPage: YourNextPage(),
exitPage: this,
//optional
backgroundColor: Colors.white,
//optional
duration: const Duration(milliseconds: 900),
),
);
Result
in this code we can easily set border radius for all corners, such as
topLeft, topRight, bottomLeft, bottomRight with one line in code for example:
borderRadius: borderRadius.evaluate(CurvedAnimation(parent: _controller, curve: Curves.ease)),
now how can i use that for separated corners? for example:
borderRadius: BorderRadius.only(
topLeft: borderRadius.evaluate(CurvedAnimation(parent: _controller, curve: Curves.ease)),
topRight: borderRadius.evaluate(CurvedAnimation(parent: _controller, curve: Curves.ease)),
),
full source code:
import 'package:flutter/material.dart';
void main() => runApp(MaterialApp(
home: HomePage(),
));
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
AnimationController _controller;
BorderRadiusTween borderRadius;
Duration _duration = Duration(milliseconds: 500);
Tween<Offset> _tween = Tween(begin: Offset(0, 1), end: Offset(0, 0));
double _height, min = 0.1, initial = 0.3, max = 1;
GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: _duration);
borderRadius = BorderRadiusTween(
begin: BorderRadius.circular(75.0),
end: BorderRadius.circular(0.0),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text('DraggableScrollableSheet'),
),
floatingActionButton: GestureDetector(
child: FloatingActionButton(
child: AnimatedIcon(icon: AnimatedIcons.menu_close, progress: _controller),
elevation: 5,
backgroundColor: Colors.deepOrange,
foregroundColor: Colors.white,
onPressed: () async {
if (_controller.isDismissed)
_controller.forward();
else if (_controller.isCompleted) _controller.reverse();
},
),
),
body: SizedBox.expand(
child: Stack(
children: <Widget>[
FlutterLogo(size: 500),
SizedBox.expand(
child: SlideTransition(
position: _tween.animate(_controller),
child: DraggableScrollableSheet(
minChildSize: min, // 0.1 times of available height, sheet can't go below this on dragging
maxChildSize: max, // 0.7 times of available height, sheet can't go above this on dragging
initialChildSize: initial, // 0.1 times of available height, sheet start at this size when opened for first time
builder: (BuildContext context, ScrollController controller) {
if (controller.hasClients) {
var dimension = controller.position.viewportDimension;
_height ??= dimension / initial;
if (dimension >= _height * max * 0.9)
_onWidgetDidBuild(() {
_scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text('ON TOP'),
duration: Duration(seconds: 3),
));
});
else if (dimension <= _height * min * 1.1)
_onWidgetDidBuild(() {
_scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text('ON BOTTOM'),
duration: Duration(seconds: 3),
));
});
}
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
return ClipRRect(
borderRadius: borderRadius.evaluate(CurvedAnimation(parent: _controller, curve: Curves.ease)),
child: Container(
color: Colors.blue[800],
child: ListView.builder(
controller: controller,
itemCount: 5,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text('Item $index'));
},
),
),
);
},
);
},
),
),
),
],
),
),
);
}
_onWidgetDidBuild(Function callback) {
WidgetsBinding.instance.addPostFrameCallback((_) {
callback();
});
}
}
You don't need to use Tween or any sort of Animation for it.
Check out this example.
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
AnimationController _controller;
Duration _duration = Duration(milliseconds: 500);
Tween<Offset> _tween = Tween(begin: Offset(0, 1), end: Offset(0, 0));
static double _origRadius = 80;
double _height, min = 0.1, initial = 0.5, max = 1, _radius = _origRadius;
GlobalKey<ScaffoldState> _key = GlobalKey();
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: _duration, value: 1);
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _key,
floatingActionButton: GestureDetector(
child: FloatingActionButton(
child: AnimatedIcon(icon: AnimatedIcons.menu_close, progress: _controller),
elevation: 5,
backgroundColor: Colors.deepOrange,
foregroundColor: Colors.white,
onPressed: () async {
if (_controller.isDismissed)
_controller.forward();
else if (_controller.isCompleted) _controller.reverse();
},
),
),
body: SizedBox.expand(
child: Stack(
children: <Widget>[
FlutterLogo(size: 500),
SizedBox.expand(
child: SlideTransition(
position: _tween.animate(_controller),
child: DraggableScrollableSheet(
minChildSize: min, // 0.1 times of available height, sheet can't go below this on dragging
maxChildSize: max, // 0.7 times of available height, sheet can't go above this on dragging
initialChildSize: initial, // 0.1 times of available height, sheet start at this size when opened for first time
builder: (BuildContext context, ScrollController controller) {
if (controller.hasClients) {
var dimension = controller.position.viewportDimension;
_height ??= dimension / initial;
// this is used for border radius
double initialTop = (_height * max) - _origRadius;
if (dimension > initialTop) {
if (_radius >= 0) {
_radius = _origRadius - (dimension - initialTop);
}
}
}
return ClipRRect(
borderRadius: BorderRadius.only(topLeft: Radius.circular(_radius), topRight: Radius.circular(_radius)),
child: Container(
height: 500.0,
color: Colors.blue[800],
child: ListView.builder(
controller: controller,
itemCount: 15,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text('Item $index'));
},
),
),
);
},
),
),
),
],
),
),
);
}
}
in this below code i want to animate change DraggableScrollableSheet border radius after that achieve to stick to top of screen such as AppBar, implemented animate change border radius for that, but it doesn't work and i get this error:
Error:
The following assertion was thrown building _BottomBarControllerScope:
'package:flutter/src/animation/animations.dart': Failed assertion:
line 376 pos 15: 'parent != null': is not true.
Either the assertion
indicates an error in the framework itself, or we should provide
substantially more information in this error message to help you
determine and fix the underlying cause. In either case, please report
this assertion by filing a bug on GitHub:
https://github.com/flutter/flutter/issues/new?template=BUG.md When the
exception was thrown, this was the stack:
2 new CurvedAnimation (package:flutter/src/animation/animations.dart:376:15)
3 _HomeState.initState (package:cheetah/screens/home/main/view/home.dart:45:7)
in that home.dart:45:7 is: CurvedAnimation in this part of code:
borderRadius = BorderRadiusTween(
begin: BorderRadius.circular(75.0),
end: BorderRadius.circular(0.0),
).animate(
CurvedAnimation(
parent: _borderRadiusController,
curve: Curves.ease,
),
);
my code:
class _HomeState extends State<Home> with TickerProviderStateMixin {
AnimationController _draggableBottomSheetController;
AnimationController _borderRadiusController;
Animation<BorderRadius> borderRadius;
Duration _duration = Duration(milliseconds: 500);
Tween<Offset> _draggableBottomSheetTween = Tween(begin: Offset(0, 1), end: Offset(0, 0));
#override
void initState() {
super.initState();
_draggableBottomSheetController = AnimationController(vsync: this, duration: _duration);
borderRadius = BorderRadiusTween(
begin: BorderRadius.circular(75.0),
end: BorderRadius.circular(0.0),
).animate(
CurvedAnimation(
parent: _borderRadiusController,
curve: Curves.ease,
),
);
}
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: AnimatedBuilder(
animation: _borderRadiusController,
builder: (BuildContext context, Widget widget){
return Scaffold(
backgroundColor: Theme.of(context).canvasColor,
extendBody: true,
primary: true,
appBar: ApplicationToolbar(title: Strings.appName),
resizeToAvoidBottomInset: false,
resizeToAvoidBottomPadding: false,
body: Container(
color: applicationBackgroundColor,
child: Stack(children: <Widget>[
Container(
width: double.infinity,
height: double.infinity,
child: PageView(
children: <Widget>[
FollowersFeedsPage(),
],
),
),
SizedBox.expand(
child: SlideTransition(
position: _draggableBottomSheetTween.animate(_draggableBottomSheetController),
child: DraggableScrollableSheet(
builder: (BuildContext context, ScrollController scrollController) {
return ClipRRect(
borderRadius: borderRadius.value,
child: Container(
color: pageBackgroundColor,
child: ListView.builder(
controller: scrollController,
itemCount: 5,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text('Item $index'));
},
),
),
);
},
),
),
),
]),
),
);
}
),
);
}
}
This should be the correct way of doing it.
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
AnimationController _controller;
BorderRadiusTween borderRadius;
Duration _duration = Duration(milliseconds: 500);
Tween<Offset> _tween = Tween(begin: Offset(0, 1), end: Offset(0, 0));
double _height, min = 0.1, initial = 0.3, max = 0.7;
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: _duration);
borderRadius = BorderRadiusTween(
begin: BorderRadius.circular(75.0),
end: BorderRadius.circular(0.0),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: GestureDetector(
child: FloatingActionButton(
child: AnimatedIcon(icon: AnimatedIcons.menu_close, progress: _controller),
elevation: 5,
backgroundColor: Colors.deepOrange,
foregroundColor: Colors.white,
onPressed: () async {
if (_controller.isDismissed)
_controller.forward();
else if (_controller.isCompleted) _controller.reverse();
},
),
),
body: SizedBox.expand(
child: Stack(
children: <Widget>[
FlutterLogo(size: 500),
SizedBox.expand(
child: SlideTransition(
position: _tween.animate(_controller),
child: DraggableScrollableSheet(
minChildSize: min, // 0.1 times of available height, sheet can't go below this on dragging
maxChildSize: max, // 0.7 times of available height, sheet can't go above this on dragging
initialChildSize: initial, // 0.1 times of available height, sheet start at this size when opened for first time
builder: (BuildContext context, ScrollController controller) {
return AnimatedBuilder(
animation: controller,
builder: (context, child) {
return ClipRRect(
borderRadius: borderRadius.evaluate(CurvedAnimation(parent: _controller, curve: Curves.ease)),
child: Container(
height: 500.0,
color: Colors.blue[800],
child: ListView.builder(
controller: controller,
itemCount: 5,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text('Item $index'));
},
),
),
);
},
);
},
),
),
),
],
),
),
);
}
}
I'm trying to figure out how to notify user with alert that comes from top of the screen like normal push notification does.
How can I alert user from top of the screen.
AlertDialog is not customizable so I'm stuck with this. Is there any way to show something like alert or snack bar from top of the screen?
Flutter gives you the possiblity to create notifications with the help of the class Overlay. To animate these entering the screen from the top you can use the SlideTransition in combination with an AnimationController. Here is an example application I created:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: Home());
}
}
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton.icon(
icon: Icon(Icons.notifications_active),
label: Text('Notify!'),
onPressed: () {
Navigator.of(context)
.overlay
.insert(OverlayEntry(builder: (BuildContext context) {
return FunkyNotification();
}));
},
),
),
);
}
}
class FunkyNotification extends StatefulWidget {
#override
State<StatefulWidget> createState() => FunkyNotificationState();
}
class FunkyNotificationState extends State<FunkyNotification>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<Offset> position;
#override
void initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 750));
position = Tween<Offset>(begin: Offset(0.0, -4.0), end: Offset.zero)
.animate(
CurvedAnimation(parent: controller, curve: Curves.bounceInOut));
controller.forward();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Material(
color: Colors.transparent,
child: Align(
alignment: Alignment.topCenter,
child: Padding(
padding: EdgeInsets.only(top: 32.0),
child: SlideTransition(
position: position,
child: Container(
decoration: ShapeDecoration(
color: Colors.deepPurple,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.0))),
child: Padding(
padding: EdgeInsets.all(10.0),
child: Text(
'Notification!',
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
),
),
),
),
),
),
);
}
}
Here you can dismiss notifications using the swipe up or down. This is the perfect notification for promotion in-app.
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> with TickerProviderStateMixin {
bool _fromTop = true;
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Icon(Icons.fireplace_outlined),
onPressed: () {
showGeneralDialog(
barrierLabel: "Label",
barrierDismissible: true,
barrierColor: Colors.transparent,
transitionDuration: Duration(milliseconds: 700),
context: context,
pageBuilder: (context, anim1, anim2) {
return GestureDetector(
onVerticalDragUpdate: (dragUpdateDetails) {
Navigator.of(context).pop();
},
child: Column(
children: [
SizedBox(height: 40),
Card(
margin:
EdgeInsets.symmetric(vertical: 20, horizontal: 10),
child: Container(
height: 100,
child: Image.asset('lib/model/promo.png',
fit: BoxFit.fill),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(40),
),
),
),
],
),
);
},
transitionBuilder: (context, anim1, anim2, child) {
return SlideTransition(
position: anim1.drive(Tween(
begin: Offset(0, _fromTop ? -1 : 1), end: Offset(0, 0))
.chain(CurveTween(curve: Sprung()))),
child: child,
);
},
);
},
),
);
}
}
class Sprung extends Curve {
factory Sprung([double damping = 20]) => Sprung.custom(damping: damping);
Sprung.custom({
double damping = 20,
double stiffness = 180,
double mass = 1.0,
double velocity = 0.0,
}) : this._sim = SpringSimulation(
SpringDescription(
damping: damping,
mass: mass,
stiffness: stiffness,
),
0.0,
1.0,
velocity,
);
final SpringSimulation _sim;
#override
double transform(double t) => _sim.x(t) + t * (1 - _sim.x(1.0));
}