最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

How to build a side panel route in flutter - Stack Overflow

programmeradmin0浏览0评论

I am trying to implement this side panel view in flutter web. I don't understand how will it work. I am using AutoRoute to route to a new page. Now this isn't a new page since I want the previous page in the back as well. How can I build it. I request you to provide some hint or idea behind the implementation

I don't want this to be a drawer for my scaffold as this is currently a separate page in mobile code. I want to convert the route to the page as a side panel now.

EDIT: Understand one main thing, I want to know if there is a way to create this side panel as a route ( just like any other page ) using which I can open this side panel by simply routing to it.

I am trying to implement this side panel view in flutter web. I don't understand how will it work. I am using AutoRoute to route to a new page. Now this isn't a new page since I want the previous page in the back as well. How can I build it. I request you to provide some hint or idea behind the implementation

I don't want this to be a drawer for my scaffold as this is currently a separate page in mobile code. I want to convert the route to the page as a side panel now.

EDIT: Understand one main thing, I want to know if there is a way to create this side panel as a route ( just like any other page ) using which I can open this side panel by simply routing to it.

Share edited Mar 4 at 5:18 Arshpreet Singh asked Mar 3 at 10:55 Arshpreet SinghArshpreet Singh 14111 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 0

I think you can take a look to the Drawer widget : https://docs.flutter.dev/cookbook/design/drawer.

You can tweak it easilly to get the same design you want ;)

Custom Side Panel in Flutter Without Using Drawer

=> Demo Video for the provided Solution: side_panel_widget

You can achieve this UI programmatically using bool showPanel for logic and Stack widget for the UI. Here's how you can do it step by step:

Step 1: Create a bool showPanel Property to Control the Panel State

bool showPanel = false; // initial panel state is colsed

Step 2: Create Methods to Open and Close the Panel

void get _openPanel {
    setState(
      () {
        showPanel = true;
      },
    );
  }

  void get _closepanel {
    setState(
      () {
        showPanel = false;
      },
    );
  }

Step 3: Build the UI Using a Stack Widget

The UI will consist of three layers:

1. Main Screen: The primary content of the page

2. Black Overlay: semi-transparent layer that appears when the panel is open Tapping it will close the panel.

3. Panel Layer: The side panel that slides in from the right

4. Open panel button: this button will open the panel

    Scaffold(
      body: Stack(
        children: <Widget>[
          const Positioned.fill(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                FlutterLogo(
                  size: 200,
                ),
                Text(
                  "Page Content",
                  style: TextStyle(
                    fontSize: 30,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ],
            ),
          ),
          Positioned(
            top: 0.0,
            right: 15.0,
            child: IconButton(
              onPressed: () {
                _openPanel;
              },
              icon: const Icon(Icons.menu),
            ),
          ),
          if (showPanel) ...{
            GestureDetector(
              onTap: () {
                _closepanel;
              },
              child: Positioned.fill(
                child: Container(
                  decoration: const BoxDecoration(
                    color: Colors.black38,
                  ),
                ),
              ),
            ),
          },
          if (showPanel) ...{
            AnimatedPositioned(
              duration: const Duration(milliseconds: 500),
              right: showPanel ? 0.0 : -90,
              top: 0.0,
              bottom: 0.0,
              child: AnimatedOpacity(
                duration: const Duration(milliseconds: 500),
                opacity: showPanel ? 1.0 : 0.0,
                child: PanelWidget(
                  onCloseTapped: () {
                    _closepanel;
                  },
                ),
              ),
            ),
          },
        ],
      ),
    );

As you can see:

  • Panel will be opened when the user tap on the menu button
  • Panel will be closed when the user tap the close button or when the user tap outside the panel
  • When showPanel true, SidePanelWidget and the simi-transparent will be visible, when it is false they will disappear

Step 4: Create the PanelWidget Component:


class PanelWidget extends StatelessWidget {
  const PanelWidget({
    super.key,
    required this.onCloseTapped,
  });

  final void Function() onCloseTapped;

  @override
  Widget build(BuildContext context) {
    return Container(
      width: MediaQuery.sizeOf(context).width * .3,
      decoration: const BoxDecoration(
        color: Colors.white,
      ),
      child: Column(
        children: <Widget>[
          SizedBox(
            height: MediaQuery.sizeOf(context).height * .1,
            child: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 15.0),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: <Widget>[
                  const Text(
                    "Goal Sammury",
                    style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  IconButton(
                    onPressed: onCloseTapped,
                    icon: const Icon(Icons.clear),
                  ),
                ],
              ),
            ),
          ),
          Expanded(
            child: Container(
              decoration: const BoxDecoration(
                color: Color(0xFFD1F8EF),
              ),
              child: const Column(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      Icon(Icons.home),
                      Text(
                        "Home Goal",
                        style: TextStyle(
                          fontSize: 19,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ],
                  ),
                  CaseSammuryWidget(),
                  LinkedAccountWidget(),
                  DeleteWidget(),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Step 5: Add Additional Components(if you need it)

1. Delete Container(DeleteWidget):


class DeleteWidget extends StatelessWidget {
  const DeleteWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(10.0),
      margin: const EdgeInsets.all(10.0),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(15.0),
      ),
      child: const Row(
        children: <Widget>[
          CircleAvatar(
            backgroundColor: Colors.redAccent,
            child: Icon(
              Icons.delete,
            ),
          ),
          Text(
            "Delete case",
            style: TextStyle(
              fontSize: 18,
              fontWeight: FontWeight.bold,
            ),
          )
        ],
      ),
    );
  }
}

2. Linked Account Widget(LinkedAccountWidget):


class LinkedAccountWidget extends StatelessWidget {
  const LinkedAccountWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(10),
      padding: const EdgeInsets.all(20.0),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(15.0),
      ),
      child: Column(
        children: <Widget>[
          const Row(
            mainAxisAlignment: MainAxisAlignment.start,
            children: <Widget>[
              Text(
                "Linked Account",
                style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          ),
          Row(
            children: <Widget>[
              const CircleAvatar(
                backgroundColor: Colors.yellow,
                child: Icon(Icons.person),
              ),
              gapW1,
              const Text(
                "Paid Saving*1111",
                style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          ),
          gapH1,
          Row(
            children: <Widget>[
              const CircleAvatar(
                backgroundColor: Colors.yellow,
                child: Icon(Icons.person),
              ),
              gapW1,
              const Text(
                "Paid Saving*1111",
                style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          )
        ],
      ),
    );
  }
}

3. The CaseSammuryWidget, with the data model that has the shown data


class CaseSammuryWidget extends StatelessWidget {
  const CaseSammuryWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(10),
      padding: const EdgeInsets.all(20.0),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(15.0),
      ),
      child: Column(
        children: <Widget>[
          const Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              Text(
                "Case Summary",
                style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                ),
              ),
              CircleAvatar(
                backgroundColor: Color(0xFFCCDF92),
                child: Padding(
                  padding: EdgeInsets.all(6.0),
                  child: Icon(
                    Icons.edit_square,
                    color: Colors.blue,
                  ),
                ),
              ),
            ],
          ),
          for (int i = 0; i < data.length; i++) ...{
            SingleRow(
              subTitle: data[i].subTitle,
              title: data[i].title,
              isLast: i == data.length - 1,
            ),
          },
        ],
      ),
    );
  }
}

class SingleRow extends StatelessWidget {
  const SingleRow({
    super.key,
    required this.subTitle,
    required this.title,
    this.isLast = false,
  });

  final String title;
  final String subTitle;
  final bool isLast;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: <Widget>[
            Text(title),
            Text(subTitle),
          ],
        ),
        if (!isLast) ...{
          const Divider(),
        }
      ],
    );
  }
}

/// Showen Data

class DataModel {
  final String title;
  final String subTitle;

  const DataModel({
    required this.title,
    required this.subTitle,
  });
}

List<DataModel> data = const <DataModel>[
  DataModel(
    title: "Estimated cost",
    subTitle: "\$1050000",
  ),
  DataModel(
    title: "Target Year",
    subTitle: "3 Year",
  ),
  DataModel(
    title: "Starting Amount",
    subTitle: "\$5000",
  ),
  DataModel(
    title: "Monthly deposit",
    subTitle: "\$500",
  ),
  DataModel(
    title: "Stratigy",
    subTitle: "Core Protifolio",
  ),
  DataModel(
    title: "Risk level",
    subTitle: "Conservative",
  ),
];

Side Panel Widget Without Drawer Full Code:


class SidePanelWidget extends StatefulWidget {
  const SidePanelWidget({super.key});

  @override
  State<SidePanelWidget> createState() => _SidePanelWidgetState();
}

class _SidePanelWidgetState extends State<SidePanelWidget> {
  bool showPanel = false;

  void get _openPanel {
    setState(
      () {
        showPanel = true;
      },
    );
  }

  void get _closepanel {
    setState(
      () {
        showPanel = false;
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: <Widget>[
          const Positioned.fill(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                FlutterLogo(
                  size: 200,
                ),
                Text(
                  "Page Content",
                  style: TextStyle(
                    fontSize: 30,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ],
            ),
          ),
          Positioned(
            top: 0.0,
            right: 15.0,
            child: IconButton(
              onPressed: () {
                _openPanel;
              },
              icon: const Icon(Icons.menu),
            ),
          ),
          if (showPanel) ...{
            GestureDetector(
              onTap: () {
                _closepanel;
              },
              child: Positioned.fill(
                child: Container(
                  decoration: const BoxDecoration(
                    color: Colors.black38,
                  ),
                ),
              ),
            ),
          },
          if (showPanel) ...{
            AnimatedPositioned(
              duration: const Duration(milliseconds: 1500),
              right: showPanel ? 0.0 : -900,
              top: 0.0,
              bottom: 0.0,
              child: AnimatedOpacity(
                duration: const Duration(milliseconds: 1500),
                opacity: showPanel ? 1.0 : 0.0,
                child: PanelWidget(
                  onCloseTapped: () {
                    _closepanel;
                  },
                ),
              ),
            ),
          },
        ],
      ),
    );
  }
}

class PanelWidget extends StatelessWidget {
  const PanelWidget({
    super.key,
    required this.onCloseTapped,
  });

  final void Function() onCloseTapped;

  @override
  Widget build(BuildContext context) {
    return Container(
      width: MediaQuery.sizeOf(context).width * .3,
      decoration: const BoxDecoration(
        color: Colors.white,
      ),
      child: Column(
        children: <Widget>[
          SizedBox(
            height: MediaQuery.sizeOf(context).height * .1,
            child: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 15.0),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: <Widget>[
                  const Text(
                    "Goal Sammury",
                    style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  IconButton(
                    onPressed: onCloseTapped,
                    icon: const Icon(Icons.clear),
                  ),
                ],
              ),
            ),
          ),
          Expanded(
            child: Container(
              decoration: const BoxDecoration(
                color: Color(0xFFD1F8EF),
              ),
              child: const Column(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      Icon(Icons.home),
                      Text(
                        "Home Goal",
                        style: TextStyle(
                          fontSize: 19,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ],
                  ),
                  CaseSammuryWidget(),
                  LinkedAccountWidget(),
                  DeleteWidget(),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class DeleteWidget extends StatelessWidget {
  const DeleteWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(10.0),
      margin: const EdgeInsets.all(10.0),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(15.0),
      ),
      child: const Row(
        children: <Widget>[
          CircleAvatar(
            backgroundColor: Colors.redAccent,
            child: Icon(
              Icons.delete,
            ),
          ),
          Text(
            "Delete case",
            style: TextStyle(
              fontSize: 18,
              fontWeight: FontWeight.bold,
            ),
          )
        ],
      ),
    );
  }
}

// Linked Account Widget

class LinkedAccountWidget extends StatelessWidget {
  const LinkedAccountWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(10),
      padding: const EdgeInsets.all(20.0),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(15.0),
      ),
      child: Column(
        children: <Widget>[
          const Row(
            mainAxisAlignment: MainAxisAlignment.start,
            children: <Widget>[
              Text(
                "Linked Account",
                style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          ),
          Row(
            children: <Widget>[
              const CircleAvatar(
                backgroundColor: Colors.yellow,
                child: Icon(Icons.person),
              ),
              gapW1,
              const Text(
                "Paid Saving*1111",
                style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          ),
          gapH1,
          Row(
            children: <Widget>[
              const CircleAvatar(
                backgroundColor: Colors.yellow,
                child: Icon(Icons.person),
              ),
              gapW1,
              const Text(
                "Paid Saving*1111",
                style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          )
        ],
      ),
    );
  }
}

// Case Sammuty Widget

class CaseSammuryWidget extends StatelessWidget {
  const CaseSammuryWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(10),
      padding: const EdgeInsets.all(20.0),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(15.0),
      ),
      child: Column(
        children: <Widget>[
          const Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              Text(
                "Case Summary",
                style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                ),
              ),
              CircleAvatar(
                backgroundColor: Color(0xFFCCDF92),
                child: Padding(
                  padding: EdgeInsets.all(6.0),
                  child: Icon(
                    Icons.edit_square,
                    color: Colors.blue,
                  ),
                ),
              ),
            ],
          ),
          for (int i = 0; i < data.length; i++) ...{
            SingleRow(
              subTitle: data[i].subTitle,
              title: data[i].title,
              isLast: i == data.length - 1,
            ),
          },
        ],
      ),
    );
  }
}

class SingleRow extends StatelessWidget {
  const SingleRow({
    super.key,
    required this.subTitle,
    required this.title,
    this.isLast = false,
  });

  final String title;
  final String subTitle;
  final bool isLast;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: <Widget>[
            Text(title),
            Text(subTitle),
          ],
        ),
        if (!isLast) ...{
          const Divider(),
        }
      ],
    );
  }
}

/// Showen Data

class DataModel {
  final String title;
  final String subTitle;

  const DataModel({
    required this.title,
    required this.subTitle,
  });
}

List<DataModel> data = const <DataModel>[
  DataModel(
    title: "Estimated cost",
    subTitle: "\$1050000",
  ),
  DataModel(
    title: "Target Year",
    subTitle: "3 Year",
  ),
  DataModel(
    title: "Starting Amount",
    subTitle: "\$5000",
  ),
  DataModel(
    title: "Monthly deposit",
    subTitle: "\$500",
  ),
  DataModel(
    title: "Stratigy",
    subTitle: "Core Protifolio",
  ),
  DataModel(
    title: "Risk level",
    subTitle: "Conservative",
  ),
];

Hope that was helpful!!

发布评论

评论列表(0)

  1. 暂无评论