GIfatahTH / states_rebuilder

a simple yet powerful state management technique for Flutter
494 stars 56 forks source link

Custom Widget #24

Closed n8crwlr closed 5 years ago

n8crwlr commented 5 years ago

Hello again,

thanks for your great support. Can i ask another question? How to provide a custom widget including bloc?

I tried serveral things, but this does not work. I have reduced a sample again (to explaining unit), which fits my needs. Would be very helpfully if you can provide some help.

import 'package:flutter/material.dart';
import 'package:states_rebuilder/states_rebuilder.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Some Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: SafeArea(
            child: Scaffold(
                body: Stack(
          children: <Widget>[
            Center(
              child: RaisedButton(
                color: Colors.pink,
                child: Text("data"),
                onPressed: () => print("button pressed"),
              ),
            ),

            // here is
            MyWidget(),
          ],
        ))));
  }
}

class MyWidget extends StatelessWidget {
  MyWidget();

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      bloc: MyWidgetBloc(),
      child: GestureDetector(
        onVerticalDragEnd: (DragEndDetails details) => bloc.end(details),
        child: StateWithMixinBuilder(
          mixinWith: MixinWith.tickerProviderStateMixin,
          blocs: [bloc],
          initState: (_, __, ticker) => bloc.initAnimation(ticker),
          dispose: (_, __, ___) => bloc.dispose(),
          builder: (context, __) => Stack(
            children: <Widget>[
              Positioned(
                left: 0,
                right: 0,
                bottom: bloc.value,
                child: Column(
                  children: <Widget>[
                    Container(color: Colors.cyan, height: 60),
                    Container(color: Colors.red, height: 200),
                    Container(color: Colors.yellow, height: 300),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class MyWidgetBloc extends StatesRebuilder {
  AnimationController _controller;

  double get value => 0;

  initAnimation(TickerProvider _ticker) {}
  end([DragEndDetails details]) {}
  dispose() => _controller.dispose();
}

I just want to load different widgets, like MyWidget into MyApp for example. I tried to provide final bloc = BlocProvider<MyWidgetBloc>(context); some way, but it only works for the linter...

As a noob, does this make any sense using states_rebuilder or should i better use StatefulWidget instead? What's about nesting?

Again, thank you for comment.

GIfatahTH commented 5 years ago

Sorry about this, but I didn't understand well what you mean by "provide a custom widget including bloc?

Where you called final bloc = BlocProvider(context);

n8crwlr commented 5 years ago

Maybe, i am wrong all in all. I thought, i can abstract states_rebuilder for single custom widgets, and compose them like other widgets.

I tried BlocProvider<MyWidgetBloc>(context) for example in the build method of MyWidget. But i didn't get a correct context, too. I tried to provide BlocProvider in MyApp, too, and paste context, bloc someway, but all in all it does not work.

I tried some different solutions, but i cannot paste any sample this time, because i actually reinstall my PC for flutter desktop shell. There is an Intel error on HD3000 and i try to get nvidia running it.

Thx

n8crwlr commented 5 years ago

Ok 👍

Now i got it having the following wrapper:

import 'package:flutter/material.dart';
import 'package:states_rebuilder/states_rebuilder.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: SafeArea(
            child: Scaffold(
                body: Stack(
          children: <Widget>[
            Center(
              child: RaisedButton(
                color: Colors.pink,
                child: Text("data"),
                onPressed: () => print("button pressed"),
              ),
            ),
            MyWidget(),
          ],
        ))));
  }
}

class MyWidgetBloc extends StatesRebuilder {
  AnimationController _controller;

  double get value => 0;

  initAnimation(TickerProvider _ticker) {}
  end([DragEndDetails details]) {}
  dispose() => _controller.dispose();
}

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      bloc: MyWidgetBloc(),
      child: MyWidgetWrapper(),
    );
  }
}

class MyWidgetWrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final bloc = BlocProvider.of<MyWidgetBloc>(context);
    return BlocProvider(
      bloc: MyWidgetBloc(),
      child: GestureDetector(
        onVerticalDragEnd: (DragEndDetails details) => bloc.end(details),
        child: StateWithMixinBuilder(
          mixinWith: MixinWith.tickerProviderStateMixin,
          blocs: [bloc],
          initState: (_, __, ticker) => bloc.initAnimation(ticker),
          dispose: (_, __, ___) => bloc.dispose(),
          builder: (context, __) => Stack(
            children: <Widget>[
              Positioned(
                left: 0,
                right: 0,
                bottom: bloc.value,
                child: Column(
                  children: <Widget>[
                    Container(color: Colors.cyan, height: 60),
                    Container(color: Colors.red, height: 200),
                    Container(color: Colors.yellow, height: 300),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

There is a wrapper now: MyWidgetWrapper.

In short:

import 'package:flutter/material.dart';
import 'package:states_rebuilder/states_rebuilder.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget { ...

class MyWidgetBloc extends StatesRebuilder { ...

class MyWidget extends StatelessWidget { ...

class MyWidgetWrapper extends StatelessWidget { ...

What do you think about? Is it okay to use? Does it make sense?

Could this become optimized?

Thank you 👍

n8crwlr commented 5 years ago

Sorry, a typo, the last one should be

class MyWidgetWrapper extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    final bloc = BlocProvider.of<TestBloc>(context);
    return GestureDetector(
        onVerticalDragUpdate: (DragUpdateDetails details) => bloc.update(details),
        onVerticalDragEnd: (DragEndDetails details) => bloc.end(details),
        onVerticalDragCancel: () => bloc.end(),
        child: StateWithMixinBuilder(
          mixinWith: MixinWith.tickerProviderStateMixin,
          blocs: [bloc],
          initState: (_, __, ticker) => bloc.initAnimation(ticker),
          dispose: (_, __, ___) => bloc.dispose(),
          builder: (context, __) => Stack(
            children: <Widget>[
              Positioned(
                left: 0,
                right: 0,
                bottom: bloc.bottom,
                child: Column(
                  children: <Widget>[
                    Container(color: Colors.cyan, width: MediaQuery.of(context).size.width, height: 60),
                    Container(color: Colors.red, width: MediaQuery.of(context).size.width, height: 200),
                    Container(color: Colors.yellow, width: MediaQuery.of(context).size.width, height: 300),
                  ],
                ),
              ),
            ],
          ),
        ),
      );
  }
}

not return BlocProvider( instead return GestureDetector(

GIfatahTH commented 5 years ago

again I didn't understand where you are going? I suggest you use Injector instead of BlocProvider. With injector, you do not have to have the context to get the model. And I think I will remove BlocProvider from the library.


class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Injector(
      models: [()=> MyWidgetBloc()],
      buuilder: (_,__)=>MyWidgetWrapper(),
    );
  }
}
```.
n8crwlr commented 5 years ago

I want to have custom widgets.

I understand 0% of Injector actually, and i do not like models / MVVM. Is this package going MVVM?

GIfatahTH commented 5 years ago

OK. This package is a is not going MVVM. It's me who goes MVVM. You can use this package with any architecture pattern you want MVC, MVP or whatever you want.

Injector is similar to get_it package that is base on IoC except that Injector is widget tree aware just like InheretedWidget. Blocs (or models) are registred when the widget is inserted into the widget tree and are disposed when they no longer needed.

with BlocProvider

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      bloc: MyWidgetBloc(),
      child: MyWidgetWrapper(),
    );
  }
}

class MyWidgetWrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final bloc = BlocProvider.of<MyWidgetBloc>(context);
    return BlocProvider( .....

you can reach the samething with Injector:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Injector(
      models: [()=>MyWidgetBloc()],
      builder: (context,model)=>MyWidgetWrapper(),
    );
  }
}

class MyWidgetWrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final bloc = Injector.get<MyWidgetBloc>();
    return BlocProvider( .....

The thing that you can not do with BlocProvide is getting the model where the context is not available for example for another bloc or class

class AnotherBloc {
final bloc = Injector.get<MyWidgetBloc>();
}

This is just for your information, feel free to close the issue if your issue is resolved

n8crwlr commented 5 years ago

Thank you very much 👍

I now changed to

class MyApp extends StatelessWidget {
  const MyApp({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: SafeArea(
            child: Scaffold(
                body: Stack(
          children: <Widget>[
            Center(
              child: RaisedButton(
                color: Colors.pink,
                child: Text("data"),
                onPressed: () => print("button pressed"),
              ),
            ),
            Injector(
              models: [() => MyWidgetBloc()],
              builder: (_, __) => MyWidget(),
            ),
          ],
        ))));
  }
}

class MyWidgetBloc extends StatesRebuilder { ...

class MyWidget extends StatelessWidget { ...

this way i only need MyWidget and MyWidgetBloc.

Do not know if this is completely ok, but i will go this way on.

n8crwlr commented 5 years ago

@GIfatahTH using Injector as shown above works great, but as of now, flutter is unable to Hot Restart - it is coredumping now every time, but Hot Reload works (as far the changes can be injected). So mostly i have to start a new debug session and wait for app startup.

What do you think? Is wrong to use this way? Should i paste the stacktrace (mostly libc) here? Or open a bug at flutter?

GIfatahTH commented 5 years ago

Although I worked extensively with Injector I haven't met with this kind of error, It would be very helpful if you post a simplified example that produces the error.

n8crwlr commented 5 years ago

Sorry, seems to be related to desktop shell.

In a new project channel stable no problems. But cannot change to master because of issue 40694 (don't want to link it).