I'm trying to get AnimatedSwitcher to work in a ReorderableListView, it works in a normal ListView. I'm thinking it has something to do with the keys, but I'm now sure.
Flutter 1.17.0 • channel beta • https://github.com/flutter/flutter.git
Framework • revision e6b34c2b5c (7 days ago) • 2020-05-02 11:39:18 -0700
Engine • revision 540786dd51
Tools • Dart 2.8.1
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => ChangeNumber(),
child: MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: SafeArea(
child: Consumer<ChangeNumber>(
builder: (context, value, child) {
return Column(
children: <Widget>[
Container(
height: 100,
child: ReorderableListView(
onReorder: (oldIndex, newIndex) {},
children: <Widget>[
AnimatedSwitcher(
key: ValueKey(value.i),
duration: Duration(seconds: 1),
child: NumberTile(
number: value.i,
key: ValueKey(value.i),
),
),
],
),
),
RaisedButton(
child: Text('Increase'),
onPressed: () => value.i = value.i + 1,
)
],
);
},
),
),
),
),
);
}
}
class NumberTile extends StatelessWidget {
final int number;
NumberTile({this.number, key}) : super(key: key);
#override
Widget build(BuildContext context) {
return ListTile(
title: Text('$number'),
);
}
}
class ChangeNumber extends ChangeNotifier {
int _i = 0;
get i => _i;
set i(int value) {
_i = value;
notifyListeners();
}
}
AnimatedSwitcher will not show effect on Text widget as your Text is inside ListTile inside NumberTile. You have to place the direct widget which you want to animate the switch inside the AnimatedSwitcher. Check this example
Related
Hi im new to flutter and making a drawing app. I making a Icon button that show overlay implemented Slider widget.
this is my code and Im using a Provider package.
main.dart
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home:
ChangeNotifierProvider(
create: (context) => DrawingProvider(),
child: BlankPage()),
),
);
}
// Main Page
class BlankPage extends StatefulWidget {
const BlankPage({Key? key}) : super(key: key);
#override
State<BlankPage> createState() => _BlankPageState();
}
class _BlankPageState extends State<BlankPage> {
#override
Widget build(BuildContext context) {
var p = Provider.of<DrawingProvider>(context);
return GestureDetector(
child: Scaffold(
appBar: AppBar(title: Text('Workbook Test')),
body: Column(
children: [
Container(
width: double.infinity,
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
penWidget(p: p),
highlighterWidget(p: p),
erasePenWidget(p: p),
SliderOverlayWidget(),
.
.
.
// ... Slider Overlay Widget I tried to make
class SliderOverlayWidget extends StatefulWidget {
const SliderOverlayWidget({
Key? key,
}) : super(key: key);
#override
State<SliderOverlayWidget> createState() => _SliderOverlayWidgetState();
}
class _SliderOverlayWidgetState extends State<SliderOverlayWidget> {
OverlayEntry? entry;
#override
void initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((_) => showOverlay() );
}
#override
Widget build(BuildContext context) {
return Container(
width: 60,
child: FittedBox(
fit: BoxFit.fitWidth,
child: GestureDetector(
onTap: () {
showOverlay();
},
child: Icon(
Icons.horizontal_rule_rounded,
color: Colors.black54,
),
),
),
);
}
void showOverlay() {
final overlay = Overlay.of(context)!;
entry = OverlayEntry(builder: (context) => buildSliderOverlay(),);
overlay.insert(entry!);
}
}
StatefulWidget buildSliderOverlay() {
return StatefulBuilder(
builder: (context, setState) {
var p = Provider.of<DrawingProvider>(context);
return Container(
width: 100,
child: Row(
children: [
Slider(value: p.penSize, onChanged: (size) {
p.changePenSize = size;
},
min: 3,
max: 15,)
],
),
);
},
);
}
DrawingProvider.dart
class DrawingProvider extends ChangeNotifier {
// pen size
double _penSize = 3;
double get penSize => _penSize;
set changePenSize(double size) {
_penSize = penSize;
notifyListeners();
}
when I run the App, errors are like
Erros
Error: Could not find the correct Provider<DrawingProvider> above this StatefulBuilder Widget
This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- You added a new provider in your `main.dart` and performed a hot-reload.
To fix, perform a hot-restart.
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
Make sure that StatefulBuilder is under your MultiProvider/Provider<DrawingProvider>.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
```
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>().toString()),
);
}
```
consider using `builder` like so:
```
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context, child) {
// No longer throws
return Text(context.watch<Example>().toString());
}
);
}
```
If none of these solutions work, consider asking for help on StackOverflow:
Thank you
I referenced this
https://www.youtube.com/watch?v=OOEyJ0ct0Sg
How to show Slider dialog widget in flutter
I have an IndexedStack in a Scaffold that I use to manage my registration. The Registration widget itself is Stateful, but the widgets that compose it are Stateless. The parent widget looks like this:
class Registration extends StatefulWidget {
#override
_RegistrationState createState() => _RegistrationState();
}
class _RegistrationState extends State<Registration> {
int _index = 0;
void _nextPage() {
setState(() {
_index++;
});
}
void _prevPage() {
setState(() {
_index--;
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.white,
appBar: new AppBar(
backgroundColor: Colors.white,
automaticallyImplyLeading: false,
leading: new IconButton(
icon: new Icon(Icons.arrow_back,
color: Theme.of(context).primaryColor),
onPressed: () {
if (_index == 0) {
Navigator.pop(context);
} else {
_prevPage();
}
}),
elevation: 0.0,
),
body: IndexedStack(
children: <Widget>[
RegistrationPhone(_nextPage),
RegistrationName(_nextPage),
RegistrationBirthday(_nextPage),],
index: _index,
),
);
}
}
What is the best way to take data from these child widgets?
Should I pass in a callback function and hold the data in the parent? Should I pass the information down the line from widget to widget until it's submitted? I don't know what the practices are for sharing data across multiple screens.
Use Provider
Add Dependency :
dependencies:
provider: ^4.3.3
here is the Example :
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
/// This is a reimplementation of the default Flutter application using provider + [ChangeNotifier].
void main() {
runApp(
/// Providers are above [MyApp] instead of inside it, so that tests
/// can use [MyApp] while mocking the providers
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => Counter()),
],
child: const MyApp(),
),
);
}
/// Mix-in [DiagnosticableTreeMixin] to have access to [debugFillProperties] for the devtool
// ignore: prefer_mixin
class Counter with ChangeNotifier, DiagnosticableTreeMixin {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
/// Makes `Counter` readable inside the devtools by listing all of its properties
#override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(IntProperty('count', count));
}
}
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Example'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text('You have pushed the button this many times:'),
/// Extracted as a separate widget for performance optimization.
/// As a separate widget, it will rebuild independently from [MyHomePage].
///
/// This is totally optional (and rarely needed).
/// Similarly, we could also use [Consumer] or [Selector].
Count(),
],
),
),
floatingActionButton: FloatingActionButton(
key: const Key('increment_floatingActionButton'),
/// Calls `context.read` instead of `context.watch` so that it does not rebuild
/// when [Counter] changes.
onPressed: () => context.read<Counter>().increment(),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class Count extends StatelessWidget {
const Count({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Text(
/// Calls `context.watch` to make [Count] rebuild when [Counter] changes.
'${context.watch<Counter>().count}',
key: const Key('counterState'),
style: Theme.of(context).textTheme.headline4);
}
}
So I'm trying to make a list that contains some widgets and then add a new widget to it when I press a button, but it doesn't seem to be working
This is the code:
class MessagesProvider extends ChangeNotifier{
List<dynamic> mesgs = [
new chatBubbleSend(),
new chatBubbleReceiver(),
new chatBubbleReceiver()
];
bool loading = true;
addMesg(){
mesgs.add(chatBubbleSend());
print(mesgs.length);
print(mesgs);
notifyListeners();
}
printMesg(){
print(mesgs.length);
print(mesgs);
}
removeMesg(){
mesgs.removeLast();
print(mesgs.length);
print(mesgs);
notifyListeners();
}
}
and this is what i get when i press the add, remove or print buttons
add,remove,print
and this is the list builder code
ChangeNotifierProvider<MessagesProvider>(
create: (context) => MessagesProvider(),
child: ChatMessages()
),
class ChatMessages extends StatelessWidget {
#override
Widget build(BuildContext context) {
final mesgs = Provider.of<MessagesProvider>(context, listen: false).mesgs;
return ListView.builder(
shrinkWrap: true,
itemCount: mesgs.length,
itemBuilder: (context,index)=> mesgs[index],
);
}
}
I have looking for a solution for over 8 hours now, and still, I couldn't fix it.
I jumped the gun with my first answer sorry.
When trying to recreate I ran into the same frustrating issue - focusing on the the provider being the problem until I realised it's actually the rendering of the updated list that's the issue.
You need to use a list builder to render the updating list in a change notifier consumer in a stateful widget
Full working example below:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class WidgetListProvider with ChangeNotifier {
List<Widget> widgets = [];
int listLength = 0;
void addWidget(){
Widget _widget = Text('Hello');
widgets.add(_widget);
listLength = widgets.length;
print('Added a widget');
notifyListeners();
}
void removeWidget(){
if (widgets.length > 0) {
widgets.removeLast();
listLength = widgets.length;
print('Removed a widget');
notifyListeners();
}
}
}
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Widget _appBar (BuildContext context) {
return AppBar(
title: Text('My App'),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: _appBar(context),
// You need to define widgets that update when a provider changes
// as children of a consumer of that provider
body: Consumer<WidgetListProvider>(builder: (context, widgetProvider, child){
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
RaisedButton(
child: Text('Add widget'),
onPressed: () {
widgetProvider.addWidget();
},
),
RaisedButton(
child: Text('Remove Widget'),
onPressed: () {
widgetProvider.removeWidget();
},
),
Row(
children: [
Text('Number of Widgets: '),
Text(widgetProvider.listLength.toString()),
],
),
Container(
height: MediaQuery.of(context).size.height*0.6,
child: ListView.builder(itemCount: widgetProvider.widgets.length, itemBuilder: (BuildContext context, int index){
return widgetProvider.widgets[index];
})
)
],
),
);
}
),
);
}
}
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => WidgetListProvider(),
child: MyApp(),
)
);
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: HomePage(),
);
}
}
I'm Ravi. I am pretty much new to Flutter but I am not new to programming. I am Stuck working with stateful widgets of Flutter. Since i am new to this SDK i am unable to figure out why my state isn't getting updated. What i did was just wrote some code that would increment a number whenever a button is clicked, And i knew that we have to use stateful widgets in order to update any data in realtime. but my code isn't working as it has to. Can anyone help me with this.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
int level = 0;
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Stateful Widget'),
),
body: Center(
child: Column(
children: [
Text('$level'),
RaisedButton(
child: Text('Increment'),
onPressed: () {
setState(() {
level = level + 1;
print(level);
});
},
),
],
),
)),
);
}
}
Console:
Performing hot reload...
Syncing files to device sdk gphone x86 arm...
Reloaded 1 of 513 libraries in 1,722ms.
I/flutter ( 8886): 1
The problem is with the scope of your level variable. It shouldn't be in your build method but in your class
class _MyAppState extends State<MyApp> {
//Moved level here.
int level = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Stateful Widget'),
),
body: Center(
child: Column(
children: [
Text('$level'),
RaisedButton(
child: Text('Increment'),
onPressed: () {
setState(() {
level = level + 1;
print(level);
});
},
),
],
),
),
),
);
}
}
I have a method in state class, but I need to access that method in outside using its widget class reference,
class TestFormState extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _testState();
}
}
class _testFormState extends State<TestFormState> {
int count = 1;
#override
Widget build(BuildContext context) {
return Center(
child: Container(
color: Colors.green,
child: Text("Count : $count"),
),
);
}
clickIncrease(){
setState(() { count += 1; });
}
}
and I need to access the above widget`s clickIncrease in another widget, like below code,
class TutorialHome extends StatelessWidget {
TestFormState test;
#override
Widget build(BuildContext context) {
// Scaffold is a layout for the major Material Components.
return Scaffold(
body: Column(
children: <Widget>[
test = TestFormState(),
FlatButton(
child: Text("Increase"),
onPressed: (){
test.state.clickIncrease(); // This kind of thing I need to do
},
),
]
),
);
}
I wrote above code just for demostrate the issue.
I have a trick, but I don't know if it is a bad practice or not.
class TestFormState extends StatefulWidget {
_TestFormState _testFormState;
#override
State<StatefulWidget> createState() {
_testFormState = _TestFormState();
return _testFormState;
}
}
class _TestFormState extends State<TestFormState> {
int count = 1;
#override
Widget build(BuildContext context) {
return Center(
child: Container(
color: Colors.green,
child: Text("Count : $count"),
),
);
}
clickIncrease(){
setState(() { count += 1; });
}
}
Now, you can access it here :
class TutorialHome extends StatelessWidget {
TestFormState test;
#override
Widget build(BuildContext context) {
// Scaffold is a layout for the major Material Components.
return Scaffold(
body: Column(
children: <Widget>[
TextButton(
child: Text("Increase"),
onPressed: () {
test._testFormState
.clickIncrease(); // This is accessable
},
),
]
),
);
}
}
I suggest taking a look at ValueNotifier
I think there is a better way to manage your app state in an easy way and I agree that using provider could be effective.
Provide the model to all widgets within the app. We're using
ChangeNotifierProvider because that's a simple way to rebuild
widgets when a model changes. We could also just use Provider, but
then we would have to listen to Counter ourselves.
Read Provider's docs to learn about all the available providers.
Initialize the model in the builder. That way, Provider can own
Counter's lifecycle, making sure to call dispose when not needed
anymore.
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(),
child: MyApp(),
),
);
}
Simplest possible model, with just one field. ChangeNotifier is a
class in flutter:foundation. Counter does not depend on Provider.
class Counter with ChangeNotifier {
int count = 1;
void clickIncrease() {
count += 1;
notifyListeners();
}
}
Consumer looks for an ancestor Provider widget and retrieves its
model (Counter, in this case). Then it uses that model to build
widgets, and will trigger rebuilds if the model is updated.
You can access your providers anywhere you have access to the context.
One way is to use Provider<Counter>.of(context).
The provider package also defines extension methods on context itself.
You can call context.watch<Counter>() in a build method of any
widget to access the current state of Counter, and to ask Flutter to
rebuild your widget anytime Counter changes.
You can't use context.watch() outside build methods, because that
often leads to subtle bugs. Instead, you should use
context.read<Counter>(), which gets the current state but doesn't
ask Flutter for future rebuilds.
Since we're in a callback that will be called whenever the user taps
the FloatingActionButton, we are not in the build method here. We
should use context.read().
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
// Scaffold is a layout for the major Material Components.
return Scaffold(
appBar: AppBar(
title: Text('Flutter Demo Home Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Count:'),
Consumer<Counter>(
builder: (context, counter, child) => Text(
'${counter.value}',
style: Theme.of(context).textTheme.headline4,
),
),
],
),
),
// I've change the button to `FloatingActionButton` for better ui experience.
floatingActionButton: FloatingActionButton(
// Here is the implementation that you are looking for.
onPressed: () {
var counter = context.read<Counter>();
counter.increment();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Complete code:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(),
child: MyApp(),
),
);
}
class Counter with ChangeNotifier {
int count = 1;
void clickIncrease() {
count += 1;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Demo Home Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Count:'),
Consumer<Counter>(
builder: (context, counter, child) => Text(
'${counter.count}',
style: Theme.of(context).textTheme.headline4,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
var counter = context.read<Counter>();
counter.clickIncrease();
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Actual app:
For more information on the provider package (where Provider comes from), please see the package documentation.
For more information on state management in Flutter, and a list of other approaches, head over to the State management page at flutter.dev.
There is a built in method findAncestorStateOfType to find Ancestor _MyAppState class of the Parent MyApp class.
Here is the Code
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
static void setLocale(BuildContext context, Locale locale) {
_MyAppState? state = context.findAncestorStateOfType<_MyAppState>();
state!.setLocale(locale);
}
#override
_MyAppState createState() => _MyAppState();
}
// ignore: use_key_in_widget_constructors
class _MyAppState extends State<MyApp> {
// const MyApp({Key? key}) : super(key: key)
late Locale _locale;
void setLocale(Locale value) {
setState(() {
_locale = value;
});
}
}
class TestForm extends StatelessWidget {
final int _count;
TestForm(int count) : _count = count;
#override
Widget build(BuildContext context) {
return Center(
child: Container(
color: Colors.green,
child: Text('Count : $_count'),
),
);
}
}
class TutorialHome extends StatefulWidget {
#override
State<TutorialHome> createState() => _TutorialHomeState();
}
class _TutorialHomeState extends State<TutorialHome> {
int _count = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
TestForm(_count), // <---
TextButton(
child: Text("Increase"),
onPressed: () => setState(() => _count++),
),
],
),
);
}
}